home *** CD-ROM | disk | FTP | other *** search
/ Developer CD Series 1997 January: Mac OS SDK / Dev.CD Jan 97 SDK2.toast / Development Kits (Disc 2) / OpenDoc / OpenDoc Development / Debugging Support / OpenDoc™ Source Code / Storage / Bento / CM / CMValOps.c < prev    next >
Encoding:
C/C++ Source or Header  |  1996-08-28  |  89.1 KB  |  2,025 lines  |  [TEXT/MPS ]

  1. /*
  2.     File:         CMValOps.c 
  3.  
  4.     Contains:    Container Manager Value Operations
  5.  
  6.     Written by:    Ira L. Ruben
  7.  
  8.     Owned by:    Ed Lai
  9.  
  10.     Copyright:    © 1991 - 1996 by Apple Computer, Inc., all rights reserved.
  11.  
  12.     Change History (most recent first):
  13.  
  14.          <4>     8/19/96    DH        1376276:OpenDoc corrupts document when
  15.                                     running out of disk space. Added function
  16.                                     to get useCount for values.
  17.          <3>     8/13/96    DM        1362809: disable containers on error
  18.          <2>     1/15/96    TJ        Cleaned Up
  19.          <4>     12/9/94    EL        #1182275 Optionally do not maintain
  20.                                     continue flag.
  21.          <3>     9/30/94    EL        #1182275 Cut down processing when size is 0
  22.                                                     in write data.
  23.          <2>     8/26/94    EL        #1182275 Cut down unnecessary read before
  24.                                                     write.
  25.          <3>      6/3/94    EL        Rename Values.h/c to ValueRtn.h/c.
  26.          <2>     3/17/94    EL        update to zero length value will not show
  27.                                                     up. For small value compare with original
  28.                                                     value to avoid unnecessary write. #1147893
  29.          <1>      2/3/94    EL        first checked in
  30.          <6>      2/1/94    EL        Check for null target container to make
  31.                                                     memory use safe for multitasking.
  32.          <5>      1/6/94    EL        Fix bug that freelist is not used if the
  33.                                                     old value has no data.
  34.          <4>     10/6/93    EL        Rename CMGetAdjacentXXXX to GetAdjacentXXXX
  35.                                                     and static near.
  36.          <3>     10/4/93    EL        Let CMGetPrevValue and CMGetNextValue share 
  37.                                                      CMGetAdjacentValue.
  38.          <2>     9/27/93    VL        Added CMGetPrevValue.
  39.  
  40.     To Do:
  41. */
  42.  
  43. /*---------------------------------------------------------------------------*
  44.  |                                                                           |
  45.  |                            <<<  CMValOps.c  >>>                           |
  46.  |                                                                           |
  47.  |                     Container Manager Value Operations                    |
  48.  |                                                                           |
  49.  |                               Ira L. Ruben                                |
  50.  |                                 12/15/91                                  |
  51.  |                                                                           |
  52.  |                  Copyright Apple Computer, Inc. 1991-1994                 |
  53.  |                           All rights reserved.                            |
  54.  |                                                                           |
  55.  *---------------------------------------------------------------------------*
  56.  
  57.  An object's property is given a typed value by writing to that value.  Conversely, you
  58.  get the value for an object's property by reading the value.  Before you do either of
  59.  these operations you must get a refNum for a value.  So depending in input or output
  60.  there are four key value operations.
  61.  
  62.      1.    CMUseValue()              returns a refNum to an existing value.
  63.     2.    CMNewValue()                creates and returns a refNum for a new value.
  64.     3.    CMReadValueData()        reads the value
  65.     4.    CMWriteValueData()    writes (sets) the value
  66.     
  67.  These routines are all defined in this file.  Other value routines are provided for doing
  68.  various manipulations and getting or setting certain information about the values.  Of
  69.  all the API routines defined in the Container Manager, you may view the CMReadValueData()
  70.  and CMWriteValueData() as two of the most important!  As it turns out they are two of the
  71.  most complicated routines in the Container Manager.  CMWriteValueData() is one of the
  72.  worst! (just thought I would worn you).
  73. */
  74.  
  75.  
  76. #include <stddef.h>
  77. #include <stdio.h>
  78. #include <stdarg.h>
  79.  
  80. #ifndef __CMTYPES__
  81. #include "CMTypes.h"
  82. #endif
  83. #ifndef __CM_API__
  84. #include "CMAPI.h"
  85. #endif
  86. #ifndef __LISTMGR__
  87. #include "ListMgr.h"
  88. #endif
  89. #ifndef __DYNAMICVALUES__
  90. #include "DynValus.h"     
  91. #endif
  92. #ifndef __VALUEROUTINES__
  93. #include "ValueRtn.h"       
  94. #endif
  95. #ifndef __TOCENTRIES__
  96. #include "TOCEnts.h"   
  97. #endif
  98. #ifndef __TOCOBJECTS__
  99. #include "TOCObjs.h"   
  100. #endif
  101. #ifndef __GLOBALNAMES__
  102. #include "GlbNames.h"   
  103. #endif
  104. #ifndef __CONTAINEROPS__
  105. #include "Containr.h"  
  106. #endif
  107. #ifndef __HANDLERS__
  108. #include "Handlers.h"
  109. #endif
  110. #ifndef __UPDATING__
  111. #include "Update.h"  
  112. #endif
  113. #ifndef __FREESPACE__
  114. #include "FreeSpce.h" 
  115. #endif
  116. #ifndef __SESSIONDATA__
  117. #include "Session.h"          
  118. #endif
  119. #ifndef __ERRORRPT__
  120. #include "ErrorRpt.h"      
  121. #endif
  122. #ifndef __UTILITYROUTINES__
  123. #include "Utility.h"        
  124. #endif
  125.  
  126.                                                                     CM_CFUNCTIONS
  127.  
  128. /* The following generates a segment directive for Mac only due to 32K Jump Table             */
  129. /* Limitations.  If you don't know what I'm talking about don't worry about it.  The        */
  130. /* default is not to generate the pragma.  Theoritically unknown pragmas in ANSI won't    */
  131. /* choke compilers that don't recognize them.                                                                                        */
  132.  
  133. #if CM_MPW
  134. #pragma segment CMValueOps
  135. #endif
  136.  
  137. /*---------------------------------------------------------------------*
  138.  | CMCountValues - count the number of values for an object's property |
  139.  *---------------------------------------------------------------------*
  140.  
  141.  A property for an object can be defined to have any number of typed values.  The types
  142.  for all the values for a given object property are unique.  This routine is used to 
  143.  determine the total number of values for a object's property or whether a value for a
  144.  given type is present for that object's property.
  145.  
  146.  If the type is specified as NULL, the total number of values for the object's property
  147.  is returned.  If the type is not NULL, 1 is returned if a value of that type is present
  148.  (because there can be a maximum of one value of that type), and 0 otherwise.  If the
  149.  property is not defined for the object, 0 is also returned.
  150. */
  151.  
  152. CMCount CM_FIXEDARGS CMCountValues(CMObject object, CMProperty property, CMType type)
  153. {
  154.     TOCPropertyPtr theProperty;
  155.     TOCValueHdrPtr theValueHdr;
  156.     ContainerPtr      container;
  157.     ContainerPtr propCtr;
  158.     
  159.     ExitIfBadObject(object, 0);                                                /* validate object                                    */
  160.     ExitIfBadProperty(property, 0);                                        /* validate property                                */
  161.     
  162.     container = ((TOCObjectPtr)object)->container;
  163.     
  164.     /* Process explicitly specified type separately...                                                                        */
  165.     
  166.     propCtr = ((TOCObjectPtr)property)->container;
  167.  
  168.     if (type != NULL) {
  169.         ExitIfBadType(type, 0);                                                    /* validate type                                        */
  170.         
  171.         { ContainerPtr typeCtr = ((TOCObjectPtr)type)->container;
  172.             if (container->targetContainer != propCtr->targetContainer ||
  173.                     container->targetContainer != typeCtr->targetContainer) {
  174.                 Container_Disable(container);
  175.                 Container_Disable(propCtr);
  176.                 Container_Disable(typeCtr);
  177.                 ERROR3(CM_err_3Containers, CONTAINERNAMEx(container),
  178.                                                                      CONTAINERNAMEx(propCtr),
  179.                                                                      CONTAINERNAMEx(typeCtr));
  180.                 return (0);
  181.             }
  182.         }
  183.         
  184.         /* Find the TOCProperty belonging to the object with the property ID of the                    */
  185.         /* specified property object...                                                                                                            */
  186.     
  187.         theProperty = cmGetObjectProperty((TOCObjectPtr)object,
  188.                                                                           ((TOCObjectPtr)property)->objectID);
  189.         if (theProperty == NULL) return (0);
  190.         
  191.         /* Find the value for the object's property that has the desired type...                        */
  192.         
  193.         theValueHdr = cmGetPropertyType(theProperty, ((TOCObjectPtr)type)->objectID);
  194.         
  195.         return ((CMCount)(theValueHdr != NULL));
  196.     }
  197.     
  198.     /* From here on we're interested in just the total number of values...                                */
  199.     
  200.     if (container->targetContainer != propCtr->targetContainer) {
  201.         Container_Disable(container);
  202.         Container_Disable(propCtr);
  203.         ERROR2(CM_err_2Containers, CONTAINERNAMEx(container),
  204.                                                              CONTAINERNAMEx(propCtr));
  205.         return (0);
  206.     }
  207.     
  208.     /* Find the TOCProperty belonging to the object with the property ID of the specified    */
  209.     /* property object...                                                                                                                                    */
  210.  
  211.     theProperty = cmGetObjectProperty((TOCObjectPtr)object,
  212.                                                                       ((TOCObjectPtr)property)->objectID);
  213.     
  214.     return (theProperty ? (CMCount)cmCountListCells(&theProperty->valueHdrList) : 0);
  215. }
  216.  
  217.  
  218. /*----------------------------------------------------------*
  219.  | CMUseValue - get a refNum for an existing value (header) |
  220.  *----------------------------------------------------------*
  221.  
  222.  This routine is used to get the refNum for the value of an object's property of the
  223.  given type.  NULL is returned if the value does not exist, or if or the object does not
  224.  contain the property.  If the type of the value corresponds to a global type name that has
  225.  an associated "use value" handler, or if it's base types (if any) have associated "use
  226.  value" handlers, a dynamic value will be created and returned. 
  227.  
  228.  Note, if the value is used as an embedded container, then that embedded container must be
  229.  opened and read using CMOpenContainer(). The data, i.e, the embedded container for such a
  230.  value can only be defined by using CMOpenNewContainer().  The container type name must be
  231.  associated with a special set of handlers that define a "return parent value" handler. 
  232.  This handler returns the parent value refNum whose data contains the embedded container.  
  233.  
  234.  There is no restriction on reading the data for an embedded container like any other value
  235.  data using CMReadValueData(). However, the data for an embedded container value includes a
  236.  TOC.  Unless a "blind" copy is being done, the TOC read this way is of not very much use!
  237. */
  238.  
  239. CMValue CM_FIXEDARGS CMUseValue(CMObject object, CMProperty property, CMType type)
  240. {
  241.     TOCPropertyPtr theProperty;
  242.     TOCValueHdrPtr theValueHdr, baseValueHdr;
  243.     ContainerPtr     container;
  244.     
  245.     ExitIfBadObject(object, NULL);                                        /* validate object                                    */
  246.     ExitIfBadProperty(property, NULL);                                /* validate property                                */
  247.     ExitIfBadType(type, NULL);                                                /* validate type                                        */
  248.     
  249.     container = ((TOCObjectPtr)object)->container;
  250.     
  251.     { ContainerPtr propCtr = ((TOCObjectPtr)property)->container;
  252.         ContainerPtr typeCtr = ((TOCObjectPtr)type)->container;
  253.         
  254.         if (container->targetContainer != propCtr->targetContainer ||
  255.                 container->targetContainer != typeCtr->targetContainer) {
  256.             Container_Disable(container);
  257.             Container_Disable(propCtr);
  258.             Container_Disable(typeCtr);
  259.             ERROR3(CM_err_3Containers, CONTAINERNAMEx(container),
  260.                                                                  CONTAINERNAMEx(propCtr),
  261.                                                                  CONTAINERNAMEx(typeCtr));
  262.             return (NULL);
  263.         }
  264.     }
  265.     
  266.     /* Find the TOCProperty belonging to the object with the property ID of the specified    */
  267.     /* property object (got that?)...                                                                                                            */
  268.     
  269.     theProperty = cmGetObjectProperty((TOCObjectPtr)object,
  270.                                                                         ((TOCObjectPtr)property)->objectID);
  271.     if (theProperty == NULL)                                                    /* if property not in object...            */
  272.         return (NULL);                                                                    /* ..tell user about it                            */
  273.     
  274.     /* If the resulting value is a dynamic value, just return it. If it isn't, this might */
  275.     /* be the first use of the value.  In that case we must check to see if a dynamic         */
  276.     /* value must be created. This is done by calling cmHasUseValueHandler() which checks */
  277.     /* to see if the value's type has a registered metahandler, and that the metahandler    */
  278.     /* defines a "use value" handler.  If that is the case, we call cmFollowTypes() to         */
  279.     /* do a depth-first processing of all the type's base types to see if layers of             */
  280.     /* dynamic values must be created.  No matter what, if a dynamic value is created, we    */
  281.     /* return that as the result.  Otherwise, the original "real" value is returned.            */
  282.  
  283.     /* Check to see if a dynamic value must be created.  This is done by calling                     */
  284.     /* cmFollowBaseTypes() to do a depth-first search starting from the given type on all    */
  285.     /* of that type's base types.  Dynamic values are created for each type that has a        */
  286.     /* "use value" handler ("new value" handlers are only required for CMNewValue()). The */
  287.     /* resulting dynamic value is returned or the "real" value if there are no dynamic         */
  288.     /* values.                                                                                                                                                        */
  289.         
  290.     theValueHdr = (TOCValueHdrPtr)cmGetPropertyType(theProperty, ((TOCObjectPtr)type)->objectID);
  291.     
  292.     if (theValueHdr) {                                                                /* if we have a value header...            */
  293.         if (theValueHdr->dynValueData.dynValue != NULL){/* ...if dynamic value exists...        */
  294.             theValueHdr = theValueHdr->dynValueData.dynValue;/*...just use it, but...                    */
  295.             ++theValueHdr->useCount;                                            /* ...count dynamic value uses            */
  296.         } else {                                                                                /* ...if no dynamic value yet...        */
  297.             baseValueHdr = theValueHdr;
  298.             theValueHdr = cmFollowTypes(theValueHdr, (TOCObjectPtr)type, false, NULL);
  299.             ++baseValueHdr->useCount;                                            /* incr use count on base value            */
  300.         }
  301.     }
  302.     
  303.     return ((CMValue)theValueHdr);                                        /* return real or dynamic value            */
  304. }
  305.  
  306.  
  307. /*--------------------------------------------------------------------------------*
  308.  | getAdjacentValue - use the adjacent value for a specified proprty of an object |
  309.  *--------------------------------------------------------------------------------*
  310.  
  311.  This routine returns the refNum for the next/prev type value (according to the current value
  312.  order) in the objects property following currValue.  If currValue is NULL, the refNum for
  313.  the first/last value for that object's property is returned.  If  currValue is not NULL, the
  314.  next/prev value for that object's property is returned.  NULL is returned if there are no more
  315.  type values following/preceding currValue or the object does not contain the property.
  316.  
  317.  currValue is generally a refNum previously returned from this routine.  Successive calls
  318.  to this routine will thus yield all the values for the specified property of the specified
  319.  object as long as no other operations change the value order.
  320.  
  321.  Note, this routine implicitly does a CMUseValue() on the returned refNum.  Thus a dynamic
  322.  value might be spawned.  A CMReleaseValue() is therfore required to reduce the use count
  323.  of the refNum.
  324. */
  325.  
  326. static CMValue CM_NEAR getAdjacentValue(CMObject object, CMProperty property, CMValue currValue,
  327.                                                                                 CMBoolean forward)
  328. {
  329.     TOCPropertyPtr theProperty;
  330.     TOCValueHdrPtr theValueHdr;
  331.     ContainerPtr      container;
  332.     CMType                 type;
  333.     
  334.     ExitIfBadObject(object, NULL);                                        /* validate object                                    */
  335.     ExitIfBadProperty(property, NULL);                                /* validate property                                */
  336.  
  337.     container = ((TOCObjectPtr)object)->container;
  338.     
  339.     { ContainerPtr propCtr = ((TOCObjectPtr)property)->container;
  340.         if (container->targetContainer != propCtr->targetContainer) {
  341.             Container_Disable(container);
  342.             Container_Disable(propCtr);
  343.             ERROR2(CM_err_2Containers, CONTAINERNAMEx(container),
  344.                                                                  CONTAINERNAMEx(propCtr));
  345.             return (NULL);
  346.         }
  347.     }
  348.  
  349.     /* Find the TOCProperty belonging to the object with the property ID of the specified    */
  350.     /* property object...                                                                                                                                    */
  351.     
  352.     theProperty = cmGetObjectProperty((TOCObjectPtr)object,
  353.                                                                         ((TOCObjectPtr)property)->objectID);
  354.     if (theProperty == NULL)                                                    /* if property not in object...            */
  355.         return (NULL);                                                                    /* ..tell user about it                            */
  356.     
  357.     /* For the first time, return the first/last value on the property's list...                    */
  358.     
  359.     if (forward)
  360.         if (currValue == NULL)                                                        /* NULL ==> use head of list            */
  361.             theValueHdr = (TOCValueHdrPtr)cmGetListHead(&theProperty->valueHdrList);
  362.         else {                                                                                        /* use next value in sequence            */
  363.             ExitIfBadValue(currValue, NULL);                                /* validate currValue                            */
  364.             theValueHdr = (TOCValueHdrPtr)cmGetNextListCell(currValue);
  365.         }
  366.     else
  367.         if (currValue == NULL)                                                        /* NULL ==> use tail of list            */
  368.             theValueHdr = (TOCValueHdrPtr)cmGetListTail(&theProperty->valueHdrList);
  369.         else {                                                                                        /* use previous value in sequence    */
  370.             ExitIfBadValue(currValue, NULL);                                /* validate currValue                            */
  371.             theValueHdr = (TOCValueHdrPtr)cmGetPrevListCell(currValue);
  372.         }
  373.     
  374.     /* We now have the value the caller wants.  But calling this routine is the "moral        */
  375.     /* equivalent" of a CMUseValue() since we are returning a value refNum.  This must be    */
  376.     /* like a CMUseValue() because the user will probably want to do a CMReleaseValue()     */
  377.     /* and also potentially spawn a dynamic value. At the very lease the CMReleaseValue()    */
  378.     /* will want the use counts to be correct.  So what we must do here is do the                 */
  379.     /* CMUseValue() for the user.  To do that we need the type object refNum.  So...            */
  380.     
  381.     if (theValueHdr != NULL) {                                                /* just to be safe!                                    */
  382.         type = (CMType)cmFindObject(container->toc, theValueHdr->typeID); /* type refNum        */
  383.         if (type == NULL)                                                                /* if should have been found!                */
  384.             theValueHdr = NULL;                                                        /* ...return NULL if it wasn't            */
  385.         else                                                                                        /* do the CMUseValue() for the user    */
  386.             theValueHdr = (TOCValueHdrPtr)CMUseValue(object, property, type);
  387.     }
  388.     
  389.     return ((CMValue)theValueHdr);                                        /* NULL or CMUseValue() result            */
  390. }
  391.  
  392.  
  393. /*--------------------------------------------------------------------------*
  394.  | CMGetNextValue - use the next value for a specified proprty of an object |
  395.  *--------------------------------------------------------------------------*
  396.  
  397.  Just call getAdjacentValue with forward.
  398. */
  399.  
  400. CMValue CM_FIXEDARGS CMGetNextValue(CMObject object, CMProperty property, CMValue currValue)
  401. {
  402.     return getAdjacentValue(object, property, currValue, true);
  403. }
  404.  
  405. /*------------------------------------------------------------------------------*
  406.  | CMGetPrevValue - use the previous value for a specified proprty of an object |
  407.  *------------------------------------------------------------------------------*
  408.  
  409.  Just call getAdjacentValue with forward = false.
  410. */
  411.  
  412. CMValue CM_FIXEDARGS CMGetPrevValue(CMObject object, CMProperty property, CMValue currValue)
  413. {
  414.     return getAdjacentValue(object, property, currValue, false);
  415. }
  416.  
  417.  
  418. /*-------------------------------------------------------*
  419.  | CMNewValue - create a refNum for a new value (header) |
  420.  *-------------------------------------------------------*
  421.  
  422.  A new value entry is created for the specified object with the specified property and type
  423.  and its refNum returned.  If the specified type corresponds to a global type name that has
  424.  an associated "use value" handler, or if its base types (if any) have associated "use
  425.  value" handlers, a dynamic value will be created and returned.
  426.  
  427.  An object's properties can have more than one value.  However, the all the types for the
  428.  values belonging to a given object property must be UNIQUE.  It is an error to attempt to
  429.  create a value for a property when there is already a value of the same type for that
  430.  property.
  431.  
  432.  Note, the refNum returned from here represents a yet-to-be-set value.  The value data is
  433.  set with CMWriteValueData(), or if it's for an embedded container, CMOpenNewContainer().
  434.  For creating embedded containers, the container type name must be associated with a set
  435.  of container handlers that support the "return parent value" handler.  Such a handler
  436.  returns the parent value refNum whose data will contain the embedded container.  No value
  437.  value data must exist for the value when the container is opened.  This value gets its
  438.  data by using it as an embedded container instead of doing CMWriteValueData()'s to it.
  439.  
  440.  Note also, the value is created at an unspecified location in the sequence of values for
  441.  the specified property.  The order of the values may change.
  442.  
  443.  As mentioned above, if the type, or any of its base types cause dynamic values to be
  444.  created, the process of creating dynamic values may required data initialization
  445.  parameters for the "new value" handler (see   DynValus.c    for a complete description of
  446.  dynamic values and the "new value" handler).  These are suppled from the CMNewValue()
  447.  "..." parameters.  They are "decoded" using a metadata format description returned from
  448.  a "metadata" handler.
  449.  
  450.  The ORDER of the parameters is important!  Because base types are done with a depth-first
  451.  search through the types down to their leaves, the CMNewValue() "..." parameters MUST be
  452.  ordered for the "deepest" type first.  For example, given the following type inheritance
  453.  heirarchy (read these as T1 inherits from T2 and T3, and so on):
  454.  
  455.                                       T4      T5
  456.                                         *    *
  457.                                          *  *
  458.                                   T2      T3
  459.                                     *    *
  460.                                      *  *
  461.                                       T1
  462.                             
  463.  The depth-first search, starting at T1, yields the sequence: T2 T4 T5 T3 T1.  Then this
  464.  is the order the CMNewValue() "..." parameters must be in.
  465.  
  466.  Implementation note: the "guts" of this routine is in CMVNewValue().  We just do what a
  467.  user would do to use that routine.
  468. */
  469.  
  470. CMValue CM_VARARGS CMNewValue(CMObject object, CMProperty property, CMType type, ...)
  471. {
  472.     CMValue value;
  473.     va_list dataInitParams;
  474.     
  475.     va_start(dataInitParams, type);                                        /* get ptr to the "..." parameters    */
  476.     value = CMVNewValue(object, property, type, dataInitParams);
  477.     va_end(dataInitParams);
  478.     
  479.     return (value);
  480. }
  481.  
  482.  
  483. /*--------------------------------------------------------*
  484.  | CMVNewValue - create a refNum for a new value (header) |
  485.  *--------------------------------------------------------*
  486.  
  487.  This routine is the same as CMNewValue() above, except that the dynamic value data
  488.  initialization (i.e., "...") parameters are given as a variable argument list as defined
  489.  by the "stdarg" facility.
  490.  
  491.  This routine assumes the caller sets up and terminates the variable arg list using the
  492.  "stdarg.h" calls as follows:
  493.  
  494.              #include <stdarg.h>
  495.             
  496.              callersRoutine(args, ...)
  497.             {
  498.                 va_list dataInitParams;
  499.                 
  500.                 - - -
  501.                 
  502.                 va_start(dataInitParams, args);
  503.                 value = CMVNewValue(object, property, type, dataInitParams);
  504.                 va_end(dataInitParams);
  505.                 
  506.                 - - -
  507.             }
  508.             
  509.  See CMNewValue() for further details.
  510. */
  511.  
  512. CMValue CM_FIXEDARGS CMVNewValue(CMObject object, CMProperty property, CMType type,
  513.                                                                   va_list dataInitParams)
  514. {
  515.     TOCObjectPtr      theObject;
  516.     TOCValueHdrPtr theValueHdr, baseValueHdr;
  517.     ContainerPtr      container;
  518.     
  519.     ExitIfBadObject(object, NULL);                                        /* validate object                                    */
  520.     ExitIfBadProperty(property, NULL);                                /* validate property                                */
  521.     ExitIfBadType(type, NULL);                                                /* validate type                                        */
  522.     
  523.     container = ((TOCObjectPtr)object)->container;
  524.     
  525.     { ContainerPtr propCtr = ((TOCObjectPtr)property)->container;
  526.         ContainerPtr typeCtr = ((TOCObjectPtr)type)->container;
  527.  
  528.         if (container->targetContainer != propCtr->targetContainer ||
  529.                 container->targetContainer != typeCtr->targetContainer) {
  530.             Container_Disable(container);
  531.             Container_Disable(propCtr);
  532.             Container_Disable(typeCtr);
  533.             ERROR3(CM_err_3Containers, CONTAINERNAMEx(container),
  534.                                                                  CONTAINERNAMEx(propCtr),
  535.                                                                  CONTAINERNAMEx(typeCtr));
  536.             return (NULL);
  537.         }
  538.     }
  539.  
  540.     container = container->updatingContainer;                /* use updating container from here on*/
  541.     
  542.     /* This will create a value header for an existing object or a new object with a             */
  543.     /* value header but no value.  Other "CM..." calls will create the actual value(s).        */
  544.     
  545.     theObject = cmDefineObject(container, ((TOCObjectPtr)object)->objectID,
  546.                                                          ((TOCObjectPtr)property)->objectID,
  547.                                                          ((TOCObjectPtr)type)->objectID,
  548.                                                          NULL, container->generation, 0, 
  549.                                                          ObjectObject, &theValueHdr);
  550.     if (theObject == NULL) return (NULL);
  551.     
  552.     /* Check to see if a dynamic value must be created.  This is done by calling                     */
  553.     /* cmFollowBaseTypes() to do a depth-first search starting from the given type on all    */
  554.     /* of that type's base types.  Dynamic values are created for each type that has a        */
  555.     /* "use value" and "new value" handler.  The resulting dynamic value is returned or        */
  556.     /* the "real" value we created above if there are no dynamic values.                                    */
  557.     
  558.     baseValueHdr = theValueHdr;
  559.     theValueHdr = cmFollowTypes(theValueHdr, (TOCObjectPtr)type, true, &dataInitParams);
  560.     
  561.     baseValueHdr->useCount = 1;                                                /* set use count of "real" value        */
  562.     
  563.     return ((CMValue)theValueHdr);                                        /* return real or dynamic value            */
  564. }
  565.  
  566.  
  567. /*--------------------------------------------------------*
  568.  | CMGetBaseValue - get the base value of a dynamic value |
  569.  *--------------------------------------------------------*
  570.  
  571.  Returns the base value for a dynamic value and NULL if the value is not a dynamic value.
  572.  It is expected that this routine will only be called from dynamic value handlers that
  573.  support layered handlers.  Indeed, this is enforced!
  574. */
  575.  
  576. CMValue CM_FIXEDARGS CMGetBaseValue(CMValue value)
  577. {
  578.     TOCValueHdrPtr theValueHdr;
  579.     ContainerPtr      container;
  580.  
  581.     ExitIfBadValue(value, NULL);                                            /* validate value                                        */
  582.     
  583.     theValueHdr = (TOCValueHdrPtr)value;
  584.     container     = theValueHdr->container;
  585.  
  586.     if (container->getBaseValueOk <= 0) {                            /* only callable from a handler            */
  587.         Container_Disable(container);
  588.         ERROR1(CM_err_CantGetBase, CONTAINERNAME);            
  589.         return (NULL);
  590.     }
  591.  
  592.     if (!IsDynamicValue(value))                                                /* if not dynamic value...                    */
  593.         return (NULL);                                                                    /* ...there is no base                            */
  594.         
  595.     return ((CMValue)DYNEXTENSIONS(value)->baseValue);/* return the base value                        */
  596. }
  597.  
  598.  
  599. /*--------------------------------------------*
  600.  | CMGetValueSize - get the size of the value |
  601.  *--------------------------------------------*
  602.  
  603.  The character size of the value data is returned.  The size returned is 0 if the value
  604.  has no data.
  605.  
  606.  Note, the size returned is the size of the data as created by CMWriteValueData() (or
  607.  CMOpenNewContainer() for embedded container values, but that's not applicable to this
  608.  discussion here).  If this data represents represents some sort of compression scheme,
  609.  the API has NO KNOWLEDGE of the actual size represented by that compressed data.
  610. */
  611.  
  612. CMSize CM_FIXEDARGS CMGetValueSize(CMValue value)
  613. {
  614.     CM_ULONG     size = 0;
  615.     
  616.     ExitIfBadValue(value, 0);                                                    /* validate value                                        */
  617.     
  618.     if (IsDynamicValue(value)) {                                            /* process dynamic value...                    */
  619.         GetDynHandlerAddress(value, cmGetValueSize, CMGetValueSizeOpType, "CMGetValueSize", 0);
  620.         if (IsDynamicValue(value)) {
  621.             SignalDynHandlerInUse(value, cmGetValueSize);
  622.             AllowCMGetBaseValue(((TOCValueHdrPtr)value)->container);    
  623.             size = (CM_ULONG)CMDynGetValueSize(value);
  624.             DisAllowCMGetBaseValue(((TOCValueHdrPtr)value)->container);    
  625.             SignalDynHandlerAvailable(value, cmGetValueSize);
  626.             return (size);
  627.         }
  628.     }
  629.         
  630.     return ((CMSize)((TOCValueHdrPtr)value)->size);        /* return size from the value hdr        */
  631. }
  632.  
  633. /*---------------------------------------------*
  634.  | CMGetValueUseCount - get refcount |
  635.  *---------------------------------------------*
  636.  
  637. */
  638.  
  639. CMCount CM_FIXEDARGS CMGetValueUseCount(CMValue value);
  640.  
  641. CMCount CM_FIXEDARGS CMGetValueUseCount(CMValue value)
  642. {
  643.     ExitIfBadValue(value, 0);                                                    /* validate value                                        */
  644.             
  645.     return ((CMCount)((TOCValueHdrPtr)value)->useCount);
  646. }
  647.  
  648. /*---------------------------------------------*
  649.  | CMReadValueData - read the data for a value |
  650.  *---------------------------------------------*
  651.  
  652.  The data, starting at the offset, for the value is read into the buffer.  The size of the
  653.  data read is returned.  Up to maxSize characters will be read (can be 0). 
  654.  
  655.  The data is read starting at the offset, up to the end of the data, or maxSize characters,
  656.  whichever comes first.  Offsets are relative to 0.  If the starting offset is greater than
  657.  or equal to the current data size, no data is read and 0 returned.
  658.  
  659.  Normally CMReadValueData() is used on a container that was opened for input using
  660.  CMOpenContainer().  However, it is permitted to read data already written to a container
  661.  that has been opened with CMOpenNewContainer().
  662.  
  663.  It is an error to attempt to read a value which has no data, i.e., a value where only a
  664.  CMNewValue() has been done.
  665. */
  666.  
  667. CMSize CM_FIXEDARGS CMReadValueData(CMValue value, CMPtr buffer, CMCount offset, CMSize maxSize)
  668. {
  669.     TOCValueHdrPtr theValueHdr;
  670.     TOCValuePtr         theValue;
  671.     ContainerPtr      container;
  672.     CM_UCHAR             *p;
  673.     CM_ULONG             len, remaining, totalRead, amountRead;
  674.  
  675.     ExitIfBadValue(value, 0);                                                    /* validate value                                        */
  676.     
  677.     container = ((TOCValueHdrPtr)value)->container;        /* NEVER use updatingContainer here!*/
  678.     
  679.     if (IsDynamicValue(value)) {                                            /* process dynamic value...                    */
  680.         GetDynHandlerAddress(value, cmReadValueData, CMReadValueDataOpType, "CMReadValueData", 0);    
  681.         if (IsDynamicValue(value)) {
  682.             SignalDynHandlerInUse(value, cmReadValueData);
  683.             AllowCMGetBaseValue(container);
  684.             totalRead = (CM_ULONG)CMDynReadValueData(value, buffer, offset, maxSize);
  685.             DisAllowCMGetBaseValue(container);    
  686.             SignalDynHandlerAvailable(value, cmReadValueData);
  687.             return (totalRead);
  688.         }
  689.     }
  690.     
  691.     theValueHdr = (TOCValueHdrPtr)value;                            /* ("this") value may have changed!    */
  692.     
  693.     #if 0    /* removed check to allow reading stuff written to an output container */
  694.     if (container->useFlags & kCMWriting) {                        /* make sure we're opend for reading*/
  695.         Container_Disable(container);
  696.         ERROR1(CM_err_ReadIllegal, CONTAINERNAME);
  697.         return (0);
  698.     }
  699.     #endif
  700.     
  701.     if (maxSize == 0 || buffer == NULL) return (0);
  702.         
  703.     /* Scan across the value list for the value header and concat data into buffer.  A        */
  704.     /* value list will exist for continued values.  The user views the data as one                */
  705.     /* contiguous chunk of stuff.  But a continued value is acutally a set of non-                */
  706.     /* contiguous smaller chunks that we concat into the user's buffer. The user specifies*/
  707.     /* a starting offset with respect to his or her view of the contiguous data.  We must    */
  708.     /* map that offset into an offest within the proper starting continued value segment.    */
  709.     /* Fortunately (yea, right) we have cmGetStartingValue() to save the day!  It does         */
  710.     /* the mapping (funny thing about that, isn't it?).                                                                        */
  711.     
  712.     if (cmIsEmptyList(&theValueHdr->valueList)) {            /* must have a value                                */
  713.         Container_Disable(container);
  714.         ERROR1(CM_err_HasNoValue, CONTAINERNAME);
  715.         return (0);
  716.     }
  717.     
  718.     theValue = cmGetStartingValue(theValueHdr, offset, &offset); /* get start seg/offset    */
  719.     if (theValue == NULL) return (0);                                    /* offset out of range                            */
  720.  
  721.     p                 = (CM_UCHAR *)buffer;                                        /* p points to next byte to read to    */
  722.     remaining = (CM_ULONG)maxSize;                                        /* remaining bytes to read                    */
  723.     totalRead = 0;                                                                        /* how much we actually do read            */
  724.  
  725.     while (theValue && remaining) {                                        /* read value (segments)...                    */
  726.         len = cmGet1ValueSize(theValue) - offset;                /* (note, immediates work here)            */
  727.         if (len > remaining) len = remaining;                        /* (note, so do global names)                */
  728.         
  729.         if (len) {                                                                            /* if more is wanted...                            */
  730.             amountRead = cmRead1ValueData(theValue, p, offset, len); /* read 1 value segment    */
  731.             totalRead += amountRead;                                            /* keep track of how much we read        */
  732.             if (amountRead < len) break;                                    /* if end of total value, we're done*/
  733.             p += len;                                                                            /* point at next free byte in buffer*/
  734.         }
  735.         
  736.         offset = 0;                                                                                /* full segments from now on            */
  737.         remaining -= len;                                                                    /* adjust what's remaining to read*/
  738.         theValue = (TOCValuePtr)cmGetNextListCell(theValue); /* point to next value seg            */
  739.     }
  740.  
  741.     return (totalRead);                                                                /* return total amount concatenated    */
  742. }
  743.  
  744.  
  745. /*------------------------------------------*
  746.  | CMWriteValueData - write data to a value |
  747.  *------------------------------------------*
  748.  
  749.  The buffer is written to the container and defined as the data for the value.  If the
  750.  value already has data associated with it, the buffer overwrites the "old" data starting
  751.  at the offset character position.  size bytes are written.  size can be 0.
  752.  
  753.  If the current size of the value data is T (it will be 0 for a new value created by
  754.  CMNewValue()), then the offset may be any value from 0 to T.  That is, existing data
  755.  may be overwritten or the value extended with new data.  The value of T can be gotton
  756.  using CMGetValueSize().  Note, no "holes" can be created.  An offset cannot be greater
  757.  than T.
  758.  
  759.  Once data has been written to the container, it may be read using CMReadValueData(). Note,
  760.  that CMReadValueData() is also used for containers opened with for input using
  761.  CMOpenContainer().  It thus can be used for all kinds of opens. The converse is not true.
  762.  CMWriteValueData() may only be used for a container opend for writing (or converting)
  763.  using CMOpenNewContainer().
  764.  
  765.  CMWriteValueData() calls for a particular value do not have to be contiguous.  Writes 
  766.  for other values can be done.  The API, specifically, this routine here, takes care of
  767.  generating "continued" value data (segments) for a value.  The data is physically not
  768.  contiguous in the container with such a case.  CMWriteValueData() hides this by allowing
  769.  the user to view the data as contiguous.  The input offset is mapped into the proper
  770.  starting segment and offset within that.
  771.  
  772.  It is an error to write to protected values.  This includes the predefined TOC objects
  773.  (seed and offset values) and objects representing currently opened embedded containers.
  774.  
  775.  For creating embedded containers, CMOPenNewContainer() is used instead of 
  776.  CMWriteValueData().  See CMNewValue() and CMOpenContainer() for further details.
  777. */
  778.  
  779. void CM_FIXEDARGS CMWriteValueData(CMValue value, CMPtr buffer, CMCount offset, CMSize size)
  780. {
  781.     TOCValueHdrPtr theValueHdr;
  782.     TOCValuePtr         theValue, theNextValue;
  783.     ContainerPtr      container;
  784.     CM_UCHAR              *p;
  785.     CM_CHAR                    offsetStr[15];
  786.     CM_ULONG             len, remaining, amountWritten, nextFree, valueSize;
  787.     TOCValueBytes  valueBytes;
  788. #if SizeReadBeforeWrite
  789.     CM_CHAR                 tempBuf[SizeReadBeforeWrite];
  790. #endif
  791.  
  792.     ExitIfBadValue(value, CM_NOVALUE);                                /* validate value                                        */
  793.     if (buffer == NULL) return;
  794.  
  795.     theValueHdr = (TOCValueHdrPtr)value;
  796.     container     = theValueHdr->container->updatingContainer;
  797.     
  798.     if ((container->useFlags & kCMWriting) == 0) {        /* make sure opened for writing            */
  799.         Container_Disable(container);
  800.         ERROR1(CM_err_WriteIllegal1, CONTAINERNAME);
  801.         return;
  802.     }
  803.     
  804.     if ((theValueHdr->valueFlags & ValueProtected) != 0){    /* can't write if protected!        */
  805.         Container_Disable(container);
  806.         ERROR1(CM_err_WriteIllegal2, CONTAINERNAME);
  807.         return;
  808.     }
  809.     
  810.     if ((theValueHdr->valueFlags & ValueGlobal) != 0) {        /* can't write to global names    */
  811.         Container_Disable(container);
  812.         ERROR1(CM_err_CantWriteGlbl, CONTAINERNAME);
  813.         return;
  814.     }
  815.  
  816.     if (IsDynamicValue(value)) {                                            /* process dynamic value...                    */
  817.         GetDynHandlerAddress(value, cmWriteValueData, CMWriteValueDataOpType, "CMWriteValueData", CM_NOVALUE);    
  818.         if (IsDynamicValue(value)) {
  819.             SignalDynHandlerInUse(value, cmWriteValueData);
  820.             AllowCMGetBaseValue(container);    
  821.             CMDynWriteValueData(value, buffer, offset, size);
  822.             DisAllowCMGetBaseValue(container);    
  823.             SignalDynHandlerAvailable(value, cmWriteValueData);
  824.             return;
  825.         }
  826.         theValueHdr = (TOCValueHdrPtr)value;                        /* ("this") value may have changed!    */
  827.     }
  828.  
  829.     valueSize = CMGetValueSize((CMValue)theValueHdr);    /* get current size of value                */
  830.     if (valueSize == 0)                                                                /* no data, treat as empty list            */
  831.         cmFreeAllListCells(&theValueHdr->valueList, SESSION);
  832.  
  833.     /* If the value list is empty, create the first or only value for this value header.    */
  834.     /* An immediate value is created if the value size is less than or equal to the             */
  835.     /* sizeof(CM_ULONG).  Otherwise we write the data to the container and set the TOC         */
  836.     /* info with the offset to it.                                                                                                                */
  837.     
  838.     if (cmIsEmptyList(&theValueHdr->valueList)) {            /* if we have a empty value list...    */
  839.         if (offset > 0) {                                                                /* ...create initial value                    */
  840.             Container_Disable(container);
  841.             ERROR2(CM_err_Offset2Big, cmltostr(offset, 1, false, offsetStr), CONTAINERNAME);
  842.             return;
  843.         }
  844.         
  845.         if (size <= sizeof(CM_ULONG)) {                                    /* we can make value immediate...        */
  846.             (void)cmSetValueBytes(container, &valueBytes, Value_Imm_Chars, (CM_ULONG)buffer, size);
  847.             cmAppendValue(theValueHdr, &valueBytes, kCMImmediate);
  848.             cmTouchImmediateValue(theValueHdr);                        /* touch for updating if necessary    */
  849.         } else if (size != 0) {                                                    /* value must be written...                    */
  850.             #if 0
  851.             nextFree = CMgetContainerSize(container);
  852.             CMfseek(container, 0, kCMSeekEnd);                                                /* position to current eof                    */
  853.             if (CMfwrite(container, buffer, sizeof(CM_UCHAR), size) != size) {
  854.                 Container_Disable(container);
  855.                 ERROR1(CM_err_BadWrite, CONTAINERNAME);
  856.                 return;
  857.             }
  858.             (void)cmSetValueBytes(container, &valueBytes, Value_NotImm, nextFree, size);
  859.             cmAppendValue(theValueHdr, &valueBytes, 0);
  860.             container->physicalEOF = nextFree + size;            /* update next free container byte    */
  861.             SetLogicalEOF(container->physicalEOF);                /* logical EOF == physical EOF            */
  862.             #endif
  863.             if ((CMSize)cmWriteData(theValueHdr, (CM_UCHAR *)buffer, (CM_ULONG)size) != size)
  864.             {    Container_Disable(container);
  865.                 ERROR1(CM_err_BadWrite, CONTAINERNAME);
  866.             }
  867.             cmTouchEditedValue(theValueHdr);                            /* touch for updating if necessary    */
  868.         }
  869.  
  870.         return;                                                                                    /* exit                                                            */
  871.     } /* end of 1st value */
  872.  
  873.     if (size == 0) return;
  874.  
  875.     /* At this point we have EXISTING data (possibly already continued).  If the offset        */
  876.     /* says that we are NOT writing to the end of the data, then we MUST be overwriting        */
  877.     /* some of the existing data.  In other words, the offset must be less than the total    */
  878.     /* size or we have an error.  We can only overwrite or append (i.e., concat).  We            */
  879.     /* cannot create a "hole". In the code that follows we consume enough of the input        */
  880.     /* buffer to do the overwriting staring at the offset.  If we consume it all, we're        */
  881.     /* done.  If not, we have reached the end of the existing value and we degenerate into*/
  882.     /* the concat case.                                                                                                                                      */
  883.     
  884.     if (valueSize != offset) {                                                /* here we must be overwriting...        */
  885.         if (TouchIt(container, theValueHdr->container)){/* if recording updates...                    */
  886. #if SizeReadBeforeWrite
  887.             /* for small write, compare with original value to skip write                                            */
  888.             if (size <= SizeReadBeforeWrite)
  889.                 if (offset+size <= theValueHdr->size)
  890.                     if (CMReadValueData(value, &tempBuf, offset, size) == size)
  891.                         if (memcmp(tempBuf, buffer, size) == 0)
  892.                             return;                                                                    /* no change, don't need to do it    */            
  893. #endif
  894.  
  895.             CMDeleteValueData(value, offset, size);                /* ...do overwrites this way!                */
  896.             CMInsertValueData(value, buffer, offset, size);
  897.             return;                                                                                /* we're through                                        */
  898.         }
  899.         
  900.         theValue  = cmGetStartingValue(theValueHdr, offset, &offset);
  901.         if (theValue == NULL) {                                                    /* offset MUST be IN the value            */
  902.             Container_Disable(container);
  903.             ERROR2(CM_err_Offset2Big, cmltostr(offset, 1, false, offsetStr), CONTAINERNAME);
  904.             return;
  905.         }
  906.     
  907.         p                 = (CM_UCHAR *)buffer;                                    /* p points to next byte to write        */
  908.         remaining = (CM_ULONG)size;                                            /* remaining bytes to write                    */
  909.     
  910.         while (theValue && remaining) {                                    /* overwrite existing value data        */
  911.             theNextValue = (TOCValuePtr)cmGetNextListCell(theValue); /* get next value seg        */
  912.             len = cmGet1ValueSize(theValue) - offset;            /* (note, immediates work here!)        */
  913.             if (len > remaining) len = remaining;
  914.             
  915.             amountWritten = cmOverwrite1ValueData(theValue, p, offset, len); /* write 1 seg.    */
  916.             if (amountWritten == 0) return;
  917.             
  918.             p += amountWritten;
  919.             
  920.             offset = 0;                                                                        /* full segments from now on                */
  921.             remaining -= amountWritten;                                        /* adjust what's remaining to write    */
  922.             theValue = theNextValue;                                             /* point to next value seg                    */
  923.         }
  924.     
  925.         if (remaining == 0) return;                                            /* yikes!  all of it was written!        */
  926.         
  927.         size = (CMSize)remaining;                                                /* prepare to write rest at end            */
  928.         buffer = (CMPtr)p;                                                            /* simply fall through to next case    */
  929.     } /* end of overwriting existing value data */
  930.  
  931.     /* At this point we want to concat the new data on to the end of the existing data.        */
  932.     /* We are doing this because the input offset was equal to the data size or we fell        */
  933.     /* through from above because there are still more bytes to write in an overwrite            */
  934.     /* and these bytes "stick" off the end of the current data.  Again this is a concat        */
  935.     /* case.  Neat isn't it?                                                                                                                            */
  936.         
  937.     /* Well, actually no!  Unlike the code above here we must handle immediate data                */
  938.     /* explicitly.  It's sort of a hassle.  That's because the amount of new data to            */
  939.     /* concat to an immediate might mean that it can't be immediate any more. Immediate        */
  940.     /* data is limited in size to less than or equal to sizeof(CM_ULONG).  If we can cram    */
  941.     /* the new data we do it. If we can't, then we must convert the immediate to a non-        */
  942.     /* immediate by writing the data to the container and changing its TOC info to be            */
  943.     /* and offset.  We then have a non-immediate which falls through to the standard            */
  944.     /* concat code for non-immediates.  The way all these cases "fall" into one another        */
  945.     /* is the "neat" thing here.                                                                                                                    */
  946.     
  947.     theValue = (TOCValuePtr)cmGetListTail(&theValueHdr->valueList); /* use tail of list        */
  948.     
  949.     if (theValue->flags & kCMImmediate) {                            /* if current value immediate...        */    
  950.         if (valueSize + size <= sizeof(CM_ULONG))    {            /* cram new data if we can...                */
  951.             if (size > 0) {                                                                /* there must be some data to write    */
  952.                 memcpy(theValue->value.imm.ucharsValue + valueSize, (CM_CHAR *)buffer, (size_t)size);
  953.                 theValue->value.notImm.valueLen = valueSize + size;
  954.                 theValueHdr->size += size;
  955.                 cmTouchImmediateValue(theValueHdr);                    /* touch for updating if necessary    */
  956.             }
  957.             return;                                                                                /* that's all we need to do!                */
  958.         }
  959.         
  960.         if (!cmConvertImmediate(theValue))                            /* convert the immediate...                    */
  961.             return;
  962.     } /* end of immediate */
  963.         
  964.     /* We are now ready to concat the new data on to the end of the existing data.  Here    */
  965.     /* too life is not simple.  A "concat" means here means a physical concat if the old    */
  966.     /* data was the last thing written to the SAME container. If it wasn't we must create */
  967.     /* a new value entry on the valueHdr's value list to represent a continued value.            */
  968.     /* Note the emphasis on "SAME" container.  We could be writing updates for an "old"        */
  969.     /* container to be recorded in a new updating container.                                                            */
  970.     
  971.     nextFree = CMgetContainerSize(container);
  972.     if (theValue->value.notImm.value + theValue->value.notImm.valueLen == nextFree &&
  973.             theValue->container == container) {                        /* remember, must be SAME container!*/
  974.         if (size > 0) {                                                                    /* there must be some data to write    */
  975.             CMfseek(container, 0, kCMSeekEnd);                        /* make sure of container position    */
  976.             if (CMfwrite(container, buffer, sizeof(CM_UCHAR), size) != size) {
  977.                 Container_Disable(container);
  978.                 ERROR1(CM_err_BadWrite, CONTAINERNAME);
  979.                 return;
  980.             }
  981.             container->physicalEOF = nextFree + size;            /* update next free container byte    */
  982.             SetLogicalEOF(container->physicalEOF);                /* logical EOF == physical EOF            */
  983.             theValue->value.notImm.valueLen += size;            /* update total size                                */
  984.             theValueHdr->size += size;                                        /* keep size in valueHdr in sync        */
  985.             cmTouchEditedValue(theValueHdr);                            /* touch for updating if necessary    */
  986.         }
  987.         return;
  988.     }
  989.     
  990.     /* Too bad!  We couldn't really do a concat.  We must create a continued value...            */
  991.     
  992.     #if 0
  993.     theValue->flags |= kCMContinued;                                    /* flag the current value as cont'd    */
  994.     theValueHdr->valueFlags |= ValueContinued;                /* also set a more convenient flag    */
  995.     
  996.     CMfseek(container, 0, kCMSeekEnd);
  997.     if (CMfwrite(container, buffer, sizeof(CM_UCHAR), size) != size) {
  998.         Container_Disable(container);
  999.         ERROR1(CM_err_BadWrite, CONTAINERNAME);
  1000.         return;
  1001.     }
  1002.     container->physicalEOF = nextFree + size;                    /* update next free container byte    */
  1003.     SetLogicalEOF(container->physicalEOF);                        /* logical EOF == physical EOF            */
  1004.     
  1005.     (void)cmSetValueBytes(container, &valueBytes, Value_NotImm, nextFree, size);
  1006.     cmAppendValue(theValueHdr, &valueBytes, 0);    
  1007.     #endif
  1008.     
  1009.     if ((CMSize)cmWriteData(theValueHdr, (CM_UCHAR *)buffer, (CM_ULONG)size) != size)
  1010.     {    Container_Disable(container);
  1011.         ERROR1(CM_err_BadWrite, CONTAINERNAME);
  1012.     }
  1013.     /* If we're recording updates, then define touched list entry for the write...                */
  1014.  
  1015.     if (size > 0) cmTouchEditedValue(theValueHdr);        /* touch for updating if necessary    */
  1016. }
  1017.  
  1018.  
  1019. /*------------------------------------------------------*
  1020.  | CMDefineValueData - define existing data for a value |
  1021.  *------------------------------------------------------*
  1022.  
  1023.  Existing data in the container is defined as the data for the value. The offset specifies
  1024.  a container offset from the beginning of the container.  No data is written to the
  1025.  container.  This call is used only to define values for stuff that is in a non-container
  1026.  opened to be converted to container format (kCMConverting useFlags passed to a 
  1027.  CMOpenNewContainer()).  It is an error to use this call in any other context.  The offset
  1028.  therefore, must be in the range of 0 to the N-1, where N is the size of preexisting data
  1029.  at the time the container was opened.
  1030.  
  1031.  Additional calls to CMDefineValueData() for the SAME value will define additional. i.e.,
  1032.  continued, segments when the offset produces noncontiguous data definition. If the size
  1033.  of the last (most recent) value segment is T, and the offset for that segment is such 
  1034.  that offset+T equals the offset for the additional segment, then the last segment is
  1035.  simply extended.  A new segment is not created.  This simulates the exact same sequence
  1036.  of events as done by CMWriteValueData().
  1037. */
  1038.  
  1039. void CM_FIXEDARGS CMDefineValueData(CMValue value, CMCount offset, CMSize size)
  1040. {
  1041.     TOCValueHdrPtr theValueHdr;
  1042.     ContainerPtr      container;
  1043.     TOCValuePtr         theValue;
  1044.     TOCValueBytes  valueBytes;
  1045.     char                     offsetStr[15];
  1046.  
  1047.     ExitIfBadValue(value, CM_NOVALUE);                                /* validate value                                        */
  1048.         
  1049.     theValueHdr = (TOCValueHdrPtr)value;
  1050.     container     = theValueHdr->container;
  1051.     
  1052.     if ((container->useFlags & kCMConverting) == 0) {    /* must be converting to a container*/
  1053.         Container_Disable(container);
  1054.         ERROR1(CM_err_NotConverting, CONTAINERNAME);
  1055.         return;
  1056.     }
  1057.     
  1058.     #if 0    /* now allow defining of additional continued value segments                                        */
  1059.     if (!cmIsEmptyList(&theValueHdr->valueList)) {        /* must not have any previous data    */
  1060.         Container_Disable(container);
  1061.         ERROR1(CM_err_HasValue, CONTAINERNAME);
  1062.         return;
  1063.     }
  1064.     #endif
  1065.     
  1066.     /* Values defined here for a file to be converted to a container must only have             */
  1067.     /* offsets within the range of the original data.  maxValueOffset was the offset to        */
  1068.     /* the last byte in the original data.  We got that when the container was opened.        */
  1069.     
  1070.     if ((CM_ULONG)offset > container->maxValueOffset ||
  1071.             (CM_ULONG)offset + size > container->maxValueOffset) {
  1072.         Container_Disable(container);
  1073.         ERROR2(CM_err_BadDefineData, cmltostr(offset, 1, false, offsetStr), CONTAINERNAME);
  1074.         return;
  1075.     }
  1076.     
  1077.     /* If this is the first value definition then no additional work need be done other     */
  1078.     /* than to create the first value segment.                                                                                        */
  1079.     
  1080.     if (cmIsEmptyList(&theValueHdr->valueList)) {            /* define 1st data segment                    */
  1081.         (void)cmSetValueBytes(container, &valueBytes, Value_NotImm, offset, size);
  1082.         cmAppendValue(theValueHdr, &valueBytes, 0);
  1083.         return;
  1084.     } /* end of 1st value */
  1085.     
  1086.     /* At this point we want to add an additional data definition to an existing value.        */
  1087.     /* We must make sure the existing value wasn't created for some other purpose, i.e.        */
  1088.     /* it can't be an immediate.                                                                                                                    */
  1089.     
  1090.     theValue = (TOCValuePtr)cmGetListTail(&theValueHdr->valueList); /* use tail of list        */
  1091.     if (theValue->flags & kCMImmediate) {                                        /* don't append to this!            */
  1092.         Container_Disable(container);
  1093.         ERROR1(CM_err_BadDefineType, CONTAINERNAME);
  1094.         return;
  1095.     }
  1096.     
  1097.     /* If the new definition is such that we can effectively do a concat, we do that             */
  1098.     /* instead of creating an additional value segment.                                                                        */
  1099.     
  1100.     if (theValue->value.notImm.value + theValue->value.notImm.valueLen == offset) {
  1101.         theValue->value.notImm.valueLen += size;                /* update total size                                */
  1102.         theValueHdr->size += size;                                            /* keep size in valueHdr in sync        */
  1103.         return;
  1104.     }
  1105.     
  1106. #if CMKEEP_CONTINUE_FLAG
  1107.     /* Have a noncontiguous definition.  So here we must create an additional continued        */
  1108.     /* value segment.                                                                                                                                            */
  1109.     
  1110.     theValue->flags |= kCMContinued;                                    /* flag the current value as cont'd    */
  1111.     theValueHdr->valueFlags |= ValueContinued;                /* also set a more convenient flag    */
  1112. #endif
  1113.     
  1114.     (void)cmSetValueBytes(container, &valueBytes, Value_NotImm, offset, size);
  1115.     cmAppendValue(theValueHdr, &valueBytes, 0);    
  1116. }
  1117.  
  1118.  
  1119. /*----------------------------------------------*
  1120.  | CMInsertValueData - insert data into a value |
  1121.  *----------------------------------------------*
  1122.  
  1123.  The buffer is inserted into the value's data at the specified offset.  size bytes are
  1124.  inserted.
  1125.  
  1126.  If the current size of the value data is T (it will be 0 for a new value created by
  1127.  CMNewValue()), then the offset may be any value from 0 to T.  That is, the insertion
  1128.  may be anywhere within the data value or the value extended with new data.  The value of
  1129.  T can be gotton using CMGetValueSize().  Note, no "holes" can be created.  An offset
  1130.  cannot be greater than T.  Also, note, that an insertion at offset T is identical to
  1131.  a CMWriteValueData() to the same place.
  1132.  
  1133.  Once data has been written to the container, it may be read using CMReadValueData(). Note,
  1134.  that CMReadValueData() is also used for containers opened with for input using
  1135.  CMOpenContainer().  It thus can be used for all kinds of opens. The converse is not true.
  1136.  CMWriteValueData() may only be used for a container opend for writing (or converting)
  1137.  using CMOpenNewContainer().
  1138.   
  1139.  It is an error to write to protected values. This includes the predefined TOC objects
  1140.  (seed and offset values) and objects representing currently opened embedded containers.
  1141.  
  1142.  For creating embedded containers, CMOPenNewContainer() is used instead of 
  1143.  CMWriteValueData().  See CMNewValue() and CMOpenContainer() for further details.
  1144.  
  1145.  From an implementation point of view, continued (i.e., segmented) values are handled by
  1146.  spliting them into one or two new segments.  The insertion is always a new segment. The
  1147.  original segment to be inserted to will split into two segments if the insertion is in
  1148.  to its middle.  It will remain intact and unsplit if we are inserting into its beginning.
  1149.  The new segment is simply inserted in front of it.  For the split case the new segment 
  1150.  must be inserted between the split pieces.
  1151. */
  1152.  
  1153. void CM_FIXEDARGS CMInsertValueData(CMValue value, CMPtr buffer, CMCount offset, CMSize size)
  1154. {
  1155.     TOCValueHdrPtr theValueHdr;
  1156.     TOCValuePtr         theValue, insBeforeValue;
  1157.     ContainerPtr      container;
  1158.     CM_UCHAR             *p, *q, *ins;
  1159.     CM_ULONG             valueSize, insSize, segOffset, insOffset, offset1, actualSize;
  1160.     char                     offsetStr[15];
  1161.  
  1162.     ExitIfBadValue(value, CM_NOVALUE);                                /* validate value                                        */
  1163.     if (size == 0 || buffer == NULL) return;
  1164.  
  1165.     theValueHdr = (TOCValueHdrPtr)value;
  1166.     container     = theValueHdr->container->updatingContainer;
  1167.     
  1168.     if ((container->useFlags & kCMWriting) == 0) {        /* make sure opened for writing            */
  1169.         Container_Disable(container);
  1170.         ERROR1(CM_err_WriteIllegal1, CONTAINERNAME);
  1171.         return;
  1172.     }
  1173.     
  1174.     if ((theValueHdr->valueFlags & ValueProtected) != 0){    /* can't write if protected!        */
  1175.         Container_Disable(container);
  1176.         ERROR1(CM_err_WriteIllegal2, CONTAINERNAME);
  1177.         return;
  1178.     }
  1179.     
  1180.     if ((theValueHdr->valueFlags & ValueGlobal) != 0) {        /* can't write to global names    */
  1181.         Container_Disable(container);
  1182.         ERROR1(CM_err_CantWriteGlbl, CONTAINERNAME);
  1183.         return;
  1184.     }
  1185.  
  1186.     if (IsDynamicValue(value)) {                                            /* process dynamic value...                    */
  1187.         GetDynHandlerAddress(value, cmInsertValueData, CMInsertValueDataOpType, "CMInsertValueData", CM_NOVALUE);    
  1188.         if (IsDynamicValue(value)) {
  1189.             SignalDynHandlerInUse(value, cmInsertValueData);
  1190.             AllowCMGetBaseValue(container);    
  1191.             CMDynInsertValueData(value, buffer, offset, size);
  1192.             DisAllowCMGetBaseValue(container);    
  1193.             SignalDynHandlerAvailable(value, cmInsertValueData);
  1194.             return;
  1195.         }
  1196.         theValueHdr = (TOCValueHdrPtr)value;                        /* ("this") value may have changed!    */
  1197.     }
  1198.     
  1199.     /* If the value list is empty, create the first or only value for this value header.    */
  1200.     /* An immediate value is created if the value size is less than or equal to the             */
  1201.     /* sizeof(CM_ULONG). Otherwise we insert (and write) the data to the container and set*/
  1202.     /* the TOC info with the offset to it.  An insert into an empty value list is exactly    */
  1203.     /* the same as writing to it the first time.  So to save some code space a call is         */
  1204.     /* made to CMWriteValueData() to do the actual work.                                                                    */
  1205.     
  1206.     if (cmIsEmptyList(&theValueHdr->valueList)) {            /* if we have a empty value list...    */
  1207.         CMWriteValueData(value, buffer, offset, size);    /* write insert (offset must be 0)    */
  1208.         return;
  1209.     } /* end of 1st value */
  1210.  
  1211.     /* At this point we have EXISTING data (possibly already continued).  If the offset        */
  1212.     /* says that we are NOT writing to the end of the data, then we MUST be inserting the    */
  1213.     /* new data.  If we are writing to the end, then the insert is exactly like a write.    */
  1214.     /* In that case CMWriteValueData() is called to do the actual work.                                        */
  1215.     
  1216.     valueSize = CMGetValueSize((CMValue)theValueHdr);    /* get current size of value                */
  1217.     
  1218.     if (valueSize == offset) {                                                /* inserting to end of current value*/
  1219.         CMWriteValueData(value, buffer, offset, size);    /* append insert to end of data            */
  1220.         return;
  1221.     } /* end of appending */
  1222.         
  1223.     /* Now we know we're actually doing an insert.  There are three cases:                                */
  1224.     
  1225.     /*    (1). Inserting into an immediate.  If the size of the immediate plus the size of    */
  1226.     /*             the insert still fits in a immediate the result will remain an immediate.        */
  1227.     /*             Otherwise, the immediate is converted to a non-immediate and we have cases        */
  1228.     /*             (2) or (3).                                                                                                                                    */
  1229.     /*    (2). Inserting into the start of an existing segment (segment offset 0).  The         */
  1230.     /*              insert is made a segment and placed before the existing segment.                            */
  1231.     /*    (3). Inserting into the middle of a non-immediate value segment.  The existing        */
  1232.     /*             segment must be split into two segments and the insert segment placed between*/
  1233.     /*             them.                                                                                                                                                */
  1234.     
  1235.     /* First case (1) -- immediates...                                                                                                        */
  1236.     
  1237.     theValue = (TOCValuePtr)cmGetListTail(&theValueHdr->valueList); /* use tail of list        */
  1238.     
  1239.     if (theValue->flags & kCMImmediate) {                            /* if current value immediate...        */    
  1240.         if (valueSize + size <= sizeof(CM_ULONG))    {            /* insert new data if we can...            */
  1241.             insSize = (CM_ULONG)size;
  1242.             p = theValue->value.imm.ucharsValue + valueSize;
  1243.             q = p + size;
  1244.             ins = theValue->value.imm.ucharsValue + offset;
  1245.             while (--p >= ins) *--q = *p;
  1246.             q = (CM_UCHAR *)buffer;
  1247.             while (size--) *++p = *q++;
  1248.             theValue->value.notImm.valueLen = valueSize + insSize;
  1249.             theValueHdr->size += insSize;
  1250.             cmTouchImmediateValue(theValueHdr);                        /* touch for updating if necessary    */
  1251.             return;                                                                                /* that's all we need to do!                */
  1252.         }
  1253.         
  1254.         if (!cmConvertImmediate(theValue))                            /* convert the immediate...                    */
  1255.             return;                                                                                /* (exit if convert failure)                */
  1256.     } /* end of immediate */
  1257.  
  1258.     /* At this point we have either cases (2) or (3).  Get the offset within the segment     */
  1259.     /* and the segment itself to determine which case we got.                                                            */
  1260.     
  1261.     insBeforeValue  = cmGetStartingValue(theValueHdr, offset, &segOffset);
  1262.     if (insBeforeValue == NULL) {                                            /* offset MUST be IN the value            */
  1263.         Container_Disable(container);
  1264.         ERROR2(CM_err_Offset2Big, cmltostr(offset, 1, false, offsetStr), CONTAINERNAME);
  1265.         return;
  1266.     }
  1267.  
  1268.     /* Write the new inserted data to the container...                                                                        */
  1269.  
  1270.     insOffset = cmGetFreeListEntry(container, (CM_ULONG)size, true, &actualSize);
  1271.     if (actualSize != 0)                                                            /* if we got some to reuse...                */
  1272.         CMfseek(container, insOffset, kCMSeekSet);            /* ...position to write over it            */
  1273.     else {                                                                                        /* if couldn't find a fit...                */
  1274.         insOffset = CMgetContainerSize(container);            /* write insert to end of container    */
  1275.         CMfseek(container, 0, kCMSeekEnd);
  1276.     }
  1277.     
  1278.     if (CMfwrite(container, buffer, sizeof(CM_UCHAR), size) != size) {
  1279.         Container_Disable(container);
  1280.         ERROR1(CM_err_BadWrite, CONTAINERNAME);
  1281.         return;
  1282.     }
  1283.     
  1284.     offset1 = insOffset + size;                                                /* update logical OR physical EOF        */
  1285.     if (actualSize == 0)
  1286.         container->physicalEOF = offset1;                                /* update next free container byte    */
  1287.     SetLogicalEOF(offset1);                                                        /* set logical EOF (may != physical)*/
  1288.     
  1289.     /* Create the new value segment to point to the inserted data and insert the segment    */
  1290.     /* into the appropriate place.  For case (2) segOffset will be 0 and for (3) non-zero.*/
  1291.     /* The actual segment create and insert is done by a separate routine. This is due to    */
  1292.     /* possible updating which uses the same routine. See comments in cmInsertNewSegment()*/
  1293.     /* (in   ValueRtn.c   ).                                                                                                                            */
  1294.     
  1295.     cmInsertNewSegment(theValueHdr, insBeforeValue, segOffset, insOffset, size);
  1296.         
  1297.     /* If we're recording updates, then define touched list entry for the insert...                */
  1298.  
  1299.     cmTouchEditedValue(theValueHdr);                                    /* touch for updating if necessary    */
  1300. }
  1301.  
  1302.  
  1303. /*----------------------------------------------*
  1304.  | CMDeleteValueData - delete data from a value |
  1305.  *----------------------------------------------*
  1306.  
  1307.  Deletes size bytes from the value data starting at the offset.  If the offset is greater
  1308.  than the value data size, nothing is deleted.  The amount to delete may be greater than
  1309.  the current data size.  In that case, all the data starting from the offset will be
  1310.  deleted.  If ALL the data is deleted, the value is defined as null, i.e. a data length of
  1311.  0.
  1312. */
  1313.  
  1314. void CM_FIXEDARGS CMDeleteValueData(CMValue value, CMCount offset, CMSize size)
  1315. {
  1316.     TOCValueHdrPtr theValueHdr;
  1317.     TOCValuePtr         theValue;
  1318.     ContainerPtr      container;
  1319.     CM_UCHAR           *p, *q;
  1320.     CM_ULONG             valueSize, startOffset, endOffset;
  1321.     
  1322.     ExitIfBadValue(value, CM_NOVALUE);                                /* validate value                                        */
  1323.     if (size == 0) return;
  1324.  
  1325.     theValueHdr = (TOCValueHdrPtr)value;
  1326.     container     = theValueHdr->container->updatingContainer;
  1327.     
  1328.     if ((container->useFlags & kCMWriting) == 0) {        /* make sure opened for writing            */
  1329.         Container_Disable(container);
  1330.         ERROR1(CM_err_WriteIllegal1, CONTAINERNAME);
  1331.         return;
  1332.     }
  1333.     
  1334.     if ((theValueHdr->valueFlags & ValueProtected) != 0){    /* can't write if protected!        */
  1335.         Container_Disable(container);
  1336.         ERROR1(CM_err_WriteIllegal2, CONTAINERNAME);
  1337.         return;
  1338.     }
  1339.     
  1340.     if ((theValueHdr->valueFlags & ValueGlobal) != 0) {        /* can't write to global names    */
  1341.         Container_Disable(container);
  1342.         ERROR1(CM_err_CantWriteGlbl, CONTAINERNAME);
  1343.         return;
  1344.     }
  1345.  
  1346.     if (IsDynamicValue(value)) {                                            /* process dynamic value...                    */
  1347.         GetDynHandlerAddress(value, cmDeleteValueData, CMDeleteValueDataOpType, "CMDeleteValueData", CM_NOVALUE);    
  1348.         if (IsDynamicValue(value)) {
  1349.             SignalDynHandlerInUse(value, cmDeleteValueData);
  1350.             AllowCMGetBaseValue(container);    
  1351.             CMDynDeleteValueData(value, offset, size);
  1352.             DisAllowCMGetBaseValue(container);    
  1353.             SignalDynHandlerAvailable(value, cmDeleteValueData);
  1354.             return;
  1355.         }
  1356.         theValueHdr = (TOCValueHdrPtr)value;                        /* ("this") value may have changed!    */
  1357.     }
  1358.     
  1359.     /* If the offset specifies that the end of the value is to be deleted, do nothing. We    */
  1360.     /* let this through since if the user doesn't want any data there s/he's got exactly    */
  1361.     /* what s/he thinks, i.e., no data there!  There never was, but who cares. Note, that    */
  1362.     /* by asking this question we also know whether any data exists for the value. If the */
  1363.     /* offset is less than the size, then the size must be non-zero and hence we have some*/
  1364.     /* deleatable data.                                                                                                                                        */
  1365.     
  1366.     valueSize = CMGetValueSize((CMValue)theValueHdr);    /* get current size of value                */
  1367.     if (offset >= valueSize) return;                                    /* if deleting from end, just exit    */
  1368.     
  1369.     /* Ok, we got to do some work and delete something.  Let's get the starting    */
  1370.     /* and ending segments and the offsets within them for starters.  [If you haven't         */
  1371.     /* guessed, I am making this algorithm up as I go along -- this should be interesting]*/
  1372.     
  1373.     startOffset = offset;                                                            /* initially get data offsets...        */
  1374.     endOffset   = startOffset + size - 1;                            /* end offset limited to end of data*/
  1375.     if (endOffset >= valueSize) 
  1376.         endOffset = valueSize - 1;
  1377.     
  1378.     /* Immediate values are handled separately...                                                                                    */
  1379.     
  1380.     theValue = (TOCValuePtr)cmGetListTail(&theValueHdr->valueList);
  1381.     if (theValue->flags & kCMImmediate) {                            /* if immediate...                                    */
  1382.         p  = theValue->value.imm.ucharsValue;                        /* ...do a simple byte delete                */
  1383.         q  = p + startOffset;
  1384.         p += endOffset + 1;
  1385.         theValueHdr->size -= endOffset - startOffset + 1;
  1386.         theValue->value.notImm.valueLen = theValueHdr->size;
  1387.         while (++endOffset < valueSize) *q++ = *p++;    
  1388.         cmTouchImmediateValue(theValueHdr);                            /* touch for updating if necessary    */
  1389.         return;                                                                                    /* that's it for immediates                    */
  1390.     }
  1391.  
  1392.     /* Delete the data from the segments.  The actual deletion is done by a separate             */
  1393.     /* routine.  This is due to possible updating which wants to use the same routine.      */
  1394.     /* See comments in cmDeleteSegmentData() (in   ValueRtn.c     ).                                                */
  1395.     
  1396.     cmDeleteSegmentData(theValueHdr, startOffset, endOffset);
  1397.     
  1398.     /* If we're recording updates, then define touched list entry for the data delete...    */
  1399.     
  1400.     cmTouchEditedValue(theValueHdr);                                    /* touch for updating if necessary    */
  1401. }
  1402.  
  1403.  
  1404. /*-------------------------------------------------------*
  1405.  | CMMoveValue - move a value from one object to another |
  1406.  *-------------------------------------------------------*
  1407.  
  1408.  Moves the specified value from its original object property to the specified object
  1409.  property.  The value is physically deleted from its original object/property as if a
  1410.  CMDeleteValue() were done on it.  If the value deleted is the only one for the property,
  1411.  the property itself is deleted as in CMDeleteObjectProperty(). 
  1412.  
  1413.  The value is added to the "to"s object propery in a manner similar to a CMNewValue().
  1414.  The order of the values for both the value's original object property and for the value's
  1415.  new object property may be changed.
  1416.  
  1417.  Note, that although the effect of a move is a combination CMDeleteValue()/CMNewValue(),
  1418.  THE INPUT REFNUM REMAINS VALID!  Its association is now with the new object property.
  1419.  
  1420.  This operation may be done at any time.  No data need be assoicated with the value at the
  1421.  time of the move.  Only moves WITHIN THE SAME CONTAINER are allowed.
  1422.  
  1423. */
  1424.  
  1425. void CM_FIXEDARGS CMMoveValue(CMValue value, CMObject object, CMProperty property)
  1426. {
  1427.     TOCValueHdrPtr theFromValueHdr, theRealValueHdr, botLayerValueHdr;
  1428.     ContainerPtr     container;
  1429.     CMObjectID         typeID;
  1430.  
  1431.     ExitIfBadValue(value, CM_NOVALUE);                                        /* validate value                                */
  1432.     
  1433.     theFromValueHdr = (TOCValueHdrPtr)value;
  1434.     container = theFromValueHdr->container;
  1435.     
  1436.     ExitIfBadObject(object, CM_NOVALUE);                                    /* validate object                            */
  1437.     ExitIfBadProperty(property, CM_NOVALUE);                            /* validate property                        */
  1438.     
  1439.     { ContainerPtr objCtr = ((TOCObjectPtr)object)->container;
  1440.         ContainerPtr propCtr = ((TOCObjectPtr)property)->container;
  1441.  
  1442.         if (container->targetContainer != objCtr->targetContainer ||
  1443.                 container->targetContainer != propCtr->targetContainer) {
  1444.             Container_Disable(container);
  1445.             Container_Disable(objCtr);
  1446.             Container_Disable(propCtr);
  1447.             ERROR3(CM_err_3Containers, CONTAINERNAMEx(container),
  1448.                                                                  CONTAINERNAMEx(objCtr),
  1449.                                                                  CONTAINERNAMEx(propCtr));
  1450.             return;
  1451.         }
  1452.     }
  1453.     
  1454.     if ((container->updatingContainer->useFlags & kCMWriting) == 0) {    
  1455.         Container_Disable(container);
  1456.         ERROR1(CM_err_MoveIllegal, CONTAINERNAME);
  1457.         return;
  1458.     }
  1459.     
  1460.     /* If the value header is a dynamic value then we must move both the dynamic value         */
  1461.     /* and its base ("real") value.  These are linked with the base value pointing to the    */
  1462.     /* its dynamic value.  For layered dynamic values, each dynamic value is back linked    */
  1463.     /* to its base with the bottom layer having the "real" value as its base.  Only the        */
  1464.     /* bottom layer is actually on the special property chain for dynamic values                     */
  1465.     /* associated with its "real value object.  Only the top layer should ever be passed. */
  1466.     /* We can check for this.  We also get the bottom layer and "real" value header             */
  1467.     /* pointers.  You will see how we use these after we get them and do the validation.    */
  1468.     
  1469.     if (IsDynamicValue(theFromValueHdr)) {                                /* if we have dynamic value...    */
  1470.         theRealValueHdr = theFromValueHdr;                                    /* start with current value            */
  1471.         do {                                                                                                /* search for base value...            */
  1472.             if ((theRealValueHdr->valueFlags & ValueOffPropChain) == 0)
  1473.                 botLayerValueHdr = theRealValueHdr;                            /* remember where bot. layer is    */
  1474.             theRealValueHdr = DYNEXTENSIONS(theRealValueHdr)->baseValue;
  1475.         } while (IsDynamicValue(theRealValueHdr));                    /* loop till we find real value    */
  1476.         
  1477.         if (theRealValueHdr->dynValueData.dynValue == NULL ||    /* do the validation...                */
  1478.                 theRealValueHdr->dynValueData.dynValue != theFromValueHdr) {
  1479.             Container_Disable(container);
  1480.             ERROR2(CM_err_BadRealValue, "CMMoveValue", CONTAINERNAME); /* ...oops!                        */
  1481.             return;
  1482.         }
  1483.         
  1484.         /* We now have the bottom (or only) layer value header and its base ("real") value. */
  1485.         /* We can move the bottom layer to the destination by simply recursively calling         */
  1486.         /* ourselves.  The bottom layer is, from a data structure point of view, a value         */
  1487.         /* header on a property chain.  All the higher layers sort of float and will still     */
  1488.         /* be pointing to the moved value.  In order though to pull this "stunt", we must     */
  1489.         /* temporarily pretend that the dynamic value isn't a dynamic value. That way we         */
  1490.         /* won't get back in this dynamic value code and will simply move it with the code     */
  1491.         /* outside this if block.  Note the object we use here is the destination object,     */
  1492.         /* but the property is the one corresponding to dynamic values.                                            */
  1493.         
  1494.         botLayerValueHdr->valueFlags &= ~ValueDynamic;            /* pretend it's not dynamic            */
  1495.         typeID = botLayerValueHdr->typeID;                                    /* hold on to the type ID...        */
  1496.         botLayerValueHdr->typeID = 0;                                                /* stop potential dup types errs*/
  1497.         CMMoveValue((CMValue)botLayerValueHdr,object,container->dynValueProperty);/* move it*/
  1498.         botLayerValueHdr->typeID = typeID;                                    /* put back type ID                            */
  1499.         botLayerValueHdr->valueFlags |= ValueDynamic;                /* put back the flag                        */
  1500.         
  1501.         /* Now it's the "real" base value's turn to be moved.  It does not need further            */
  1502.         /* validating.  So all we need to do is "fall" through to the normal value case            */
  1503.         /* below.  It will move to the destination and be placed on the desired property         */
  1504.         /* chain.  The dynamic value (layer) is there "waiting" for it.  Since all the             */
  1505.         /* refNums remain the same and so do the fields (except for the property links of     */
  1506.         /* course), everything is properly linked.                                                                                    */
  1507.         
  1508.         theFromValueHdr = theRealValueHdr;                                    /* pretend base was passed            */
  1509.     } /* dynamic value */
  1510.     
  1511.     /* If we're recording updates, then define touched list entry for the move. This MUST */
  1512.     /* be done before the move so that the touch list entry can be generated in terms of     */
  1513.     /* the original object, property, and type.  The move can change them.                                */
  1514.     
  1515.     cmTouchMovedValue(theFromValueHdr, theFromValueHdr->theProperty->theObject,
  1516.                                         (TOCObjectPtr)object, ((TOCObjectPtr)property)->objectID);
  1517.     
  1518.     /* Move the value (header).  The actual move is done by a separate routine.  This is     */
  1519.     /* due to possible updating which wants to use the same routine.  See comments in         */
  1520.     /* cmMoveValueHdr() (in   ValueRtn.c   ).                                                                                            */
  1521.     
  1522.     cmMoveValueHdr(theFromValueHdr, object, property);
  1523. }
  1524.  
  1525.  
  1526. /*--------------------------------------------*
  1527.  | CMGetValueInfo - return info about a value |
  1528.  *--------------------------------------------*
  1529.  
  1530.  The specified information for the refNum associated with a value is returned. A parameter
  1531.  may be NULL indicating that info is not needed.
  1532. */
  1533.  
  1534. void CM_FIXEDARGS CMGetValueInfo(CMValue value, CMContainer CM_PTR *container0,
  1535.                                                                  CMObject CM_PTR *object, CMProperty CM_PTR *property,
  1536.                                                                  CMType CM_PTR *type, CMGeneration CM_PTR *generation)
  1537. {
  1538.     TOCValueHdrPtr theValueHdr;
  1539.     ContainerPtr     container;                                                    /* this is to make error macro work    */
  1540.     
  1541.     ExitIfBadValue(value, CM_NOVALUE);                                /* validate value                                        */
  1542.     
  1543.     container = ((TOCValueHdrPtr)value)->container;
  1544.  
  1545.     if (IsDynamicValue(value)) {                                            /* process dynamic value...                    */
  1546.         GetDynHandlerAddress(value, cmGetValueInfo, CMGetValueInfoOpType, "CMGetValueInfo", CM_NOVALUE);    
  1547.         if (IsDynamicValue(value)) {
  1548.             SignalDynHandlerInUse(value, cmGetValueInfo);
  1549.             AllowCMGetBaseValue(container);    
  1550.             CMDynGetValueInfo(value, container0, object, property, type, generation);
  1551.             DisAllowCMGetBaseValue(container);    
  1552.             SignalDynHandlerAvailable(value, cmGetValueInfo);
  1553.             return;
  1554.         }
  1555.     }
  1556.         
  1557.     theValueHdr = (TOCValueHdrPtr)value;                            /* ("this") value may have changed!    */
  1558.     
  1559.     if (container0)                                                                     /* container...                                            */
  1560.         *container0 = (CMContainer)container;                    
  1561.     
  1562.     if (object)                                                                                /* object...                                                */
  1563.         *object = (CMObject)theValueHdr->theProperty->theObject;
  1564.     
  1565.     if (property)                                                                            /* property...                                            */
  1566.         *property = (CMProperty)cmFindObject(container->toc, theValueHdr->theProperty->propertyID);
  1567.     
  1568.     if (type)                                                                                 /* type...                                                    */
  1569.         *type = (CMType)cmFindObject(container->toc, theValueHdr->typeID);
  1570.         
  1571.     if (generation)                                                                        /* generation...                                        */
  1572.         *generation = theValueHdr->generation;
  1573. }
  1574.  
  1575.  
  1576. /*------------------------------------------*
  1577.  | CMSetValueType - set the type of a value |
  1578.  *------------------------------------------*
  1579.  
  1580.  The type ID from the type is set for the specified value.
  1581.  
  1582.  What do you know?  A short comment!
  1583. */
  1584.  
  1585. void CM_FIXEDARGS CMSetValueType(CMValue value, CMType type)
  1586. {
  1587.     TOCValueHdrPtr theValueHdr, theValueHdr0;
  1588.     ContainerPtr     container;
  1589.     CM_ULONG             typeID;
  1590.     
  1591.     ExitIfBadValue(value, CM_NOVALUE);                                /* validate value                                        */
  1592.     ExitIfBadType(type, CM_NOVALUE);                                    /* validate type                                        */
  1593.     
  1594.     container = ((TOCValueHdrPtr)value)->container;
  1595.     
  1596.     { ContainerPtr typeCtr = ((TOCObjectPtr)type)->container;
  1597.  
  1598.         if (container->targetContainer != typeCtr->targetContainer) {
  1599.             Container_Disable(container);
  1600.             Container_Disable(typeCtr);
  1601.             ERROR2(CM_err_2Containers, CONTAINERNAMEx(container),
  1602.                                                                  CONTAINERNAMEx(typeCtr));
  1603.             return;
  1604.         }
  1605.     }
  1606.  
  1607.     if (IsDynamicValue(value)) {                                            /* process dynamic value...                    */
  1608.         GetDynHandlerAddress(value, cmSetValueType, CMSetValueTypeOpType, "CMSetValueType", CM_NOVALUE);    
  1609.         if (IsDynamicValue(value)) {
  1610.             SignalDynHandlerInUse(value, cmSetValueType);
  1611.             AllowCMGetBaseValue(container);    
  1612.             CMDynSetValueType(value, type);
  1613.             DisAllowCMGetBaseValue(container);    
  1614.             SignalDynHandlerAvailable(value, cmSetValueType);
  1615.             return;
  1616.         }
  1617.     }
  1618.     
  1619.     theValueHdr = (TOCValueHdrPtr)value;                            /* ("this") value may have changed!    */
  1620.     
  1621.     /* Make sure the type doesn't already exist for theobject's property...                                */
  1622.     
  1623.     typeID = ((TOCObjectPtr)type)->objectID;
  1624.     
  1625.     theValueHdr0 = cmGetPropertyType(theValueHdr->theProperty, typeID);
  1626.     if (theValueHdr0 != NULL && theValueHdr != theValueHdr0) {
  1627.         Container_Disable(container);
  1628.         ERROR2(CM_err_DupType, cmGetGlobalTypeName(container, typeID), CONTAINERNAME);
  1629.         return;
  1630.     }
  1631.  
  1632.     /* If we're recording updates, then define touched list entry for the set-info.  This    */
  1633.     /* touch MUST be done before any changes to the type because the touch list info is      */
  1634.     /* in terms of the original type.                                                                                                            */
  1635.  
  1636.     cmTouchSetInfoedValue(theValueHdr);                                /* touch the value if necessary            */
  1637.  
  1638.     /* Change the type...                                                                                                                                    */
  1639.     
  1640.     theValueHdr->typeID = typeID;                                            /* there goes the type                            */
  1641. }
  1642.  
  1643.  
  1644. /*-------------------------------------------------------------*
  1645.  | CMSetValueGeneration - set the generation number of a value |
  1646.  *-------------------------------------------------------------*
  1647.  
  1648.  The generation for the specified value is set.  The generation number must be greater than
  1649.  or equal to 1.
  1650.  
  1651.  Normally this routine doesn't need to be used since the generation is set when a value is
  1652.  created.  The default generation number is that of the container.
  1653. */
  1654.  
  1655. void CM_FIXEDARGS CMSetValueGeneration(CMValue value, CMGeneration generation)
  1656. {
  1657.     TOCValueHdrPtr theValueHdr;
  1658.     ContainerPtr     container;
  1659.     char                     genStr[15];
  1660.     
  1661.     ExitIfBadValue(value, CM_NOVALUE);                                /* validate value                                        */
  1662.     
  1663.     container = ((TOCValueHdrPtr)value)->container;
  1664.  
  1665.     if (IsDynamicValue(value)) {                                            /* process dynamic value...                    */
  1666.         GetDynHandlerAddress(value, cmSetValueGen, CMSetValueGenOpType, "CMSetValueGeneration", CM_NOVALUE);    
  1667.         if (IsDynamicValue(value)) {
  1668.             SignalDynHandlerInUse(value, cmSetValueGen);
  1669.             AllowCMGetBaseValue(container);    
  1670.             CMDynSetValueGen(value, generation);
  1671.             DisAllowCMGetBaseValue(container);    
  1672.             SignalDynHandlerAvailable(value, cmSetValueGen);
  1673.             return;
  1674.         }
  1675.     }
  1676.     
  1677.     theValueHdr = (TOCValueHdrPtr)value;                            /* ("this") value may have changed!    */
  1678.  
  1679.     /* If we're recording updates, then define touched list entry for the set-info.  This    */
  1680.     /* touch MUST be done before any changes to the type because the touch list info is      */
  1681.     /* in terms of the original type.                                                                                                            */
  1682.  
  1683.     cmTouchSetInfoedValue(theValueHdr);                                /* touch the value if necessary            */
  1684.  
  1685.     /* Change generation...                                                                                                                                */
  1686.     
  1687.     if (generation < 1) {                                                            /* validate the generation nbr            */
  1688.         Container_Disable(container);
  1689.         ERROR3(CM_err_BadGenNbr, cmltostr(generation, 1, false, genStr), "CMSetValueGeneration", CONTAINERNAME);
  1690.         return;
  1691.     }
  1692.             
  1693.     theValueHdr->generation = generation;                            /* finally, set the generation nbr    */
  1694. }
  1695.  
  1696.  
  1697. /*----------------------------------------------------------------------*
  1698.  | CMGetValueContainer - get the container refNum for one of its values |
  1699.  *----------------------------------------------------------------------*
  1700.  
  1701.  The refNum for the container which contains the specified value is returned.
  1702.  
  1703.  Note that CMGetValueInfo() also returns the container refNum.  But this is generally
  1704.  useful enough on its own (especially in handlers) to warrent a separate API call. 
  1705.  Further, it provides a symmetry with CMGetObjectContainer().
  1706. */
  1707.  
  1708. CMContainer CM_FIXEDARGS CMGetValueContainer(CMValue value)
  1709. {
  1710.     ExitIfBadValue((TOCValueHdrPtr)value, NULL);                            /* validate value                        */
  1711.     
  1712.     return ((CMContainer)((TOCValueHdrPtr)value)->container); /* return container                    */
  1713. }
  1714.  
  1715.  
  1716. /*-------------------------------------------------*
  1717.  | CMGetValueRefCon - return user's value "refCon" |
  1718.  *-------------------------------------------------*
  1719.  
  1720.  This routine returns the user's "refCon" (reference constant) that s/he may associate
  1721.  with any value refNum (i.e., a CMValue).  The refCon is a CM_ULONG that the user may use
  1722.  in any way.  It is not touched by the API except to init it to 0 when the value is
  1723.  initially created.
  1724.  
  1725.  Note, the refCon is NOT perserved across closed containers, i.e., it is not saved in the
  1726.  TOC.
  1727. */
  1728.  
  1729. CMRefCon CM_FIXEDARGS CMGetValueRefCon(CMValue value)
  1730. {
  1731.     ExitIfBadValue((TOCValueHdrPtr)value, (CMRefCon)0);                /* validate value                        */
  1732.     
  1733.     return (((TOCValueHdrPtr)value)->valueRefCon);                        /* return user's refCon            */
  1734. }
  1735.  
  1736.  
  1737. /*--------------------------------------------------*
  1738.  | CMSetValueRefCon - set the user's value "refCon" |
  1739.  *--------------------------------------------------*
  1740.  
  1741.  This routine is used to set the user's "refCon" (reference constant) to be assoicated with
  1742.  an value.  The refCon is a CM_ULONG that the user may use in any way. It is not touched by
  1743.  the API.
  1744.  
  1745.  Note, the refCon is NOT perserved across closed containers, i.e., it is not saved in the
  1746.  TOC.
  1747. */
  1748.  
  1749. void CM_FIXEDARGS CMSetValueRefCon(CMValue value, CMRefCon refCon)
  1750. {
  1751.     ExitIfBadValue((TOCValueHdrPtr)value, CM_NOVALUE);                /* validate value                        */
  1752.     
  1753.     ((TOCValueHdrPtr)value)->valueRefCon = refCon;                        /* set the user's refCon        */
  1754. }
  1755.  
  1756.  
  1757. /*--------------------------------*
  1758.  | CMDeleteValue - delete a value |
  1759.  *--------------------------------*
  1760.  
  1761.  The value is deleted from its object property.  If the value deleted is the only one for
  1762.  the property, the property itself is deleted as in CMDeleteObjectProperty().
  1763.  
  1764.  Calling CMDeleteValue() produces an implicit call on CMReleaseValue().  Hence the passed
  1765.  value refNum's association to the value is destroyed.  There should be no further
  1766.  operations on the refNum once CMDeleteValue() is called.
  1767.  
  1768.  Note, some values are protected from deletion.  This includes the predefined TOC object
  1769.  values (seed and offset) and the currently open embedded container values.  Also, a value
  1770.  acting as a base value for a subvalue cannot be deleted.
  1771. */
  1772.  
  1773. void CM_FIXEDARGS CMDeleteValue(CMValue value)
  1774. {
  1775.     TOCValueHdrPtr theValueHdr, theRealValueHdr;
  1776.     ContainerPtr     container;
  1777.  
  1778.     ExitIfBadValue(value, CM_NOVALUE);                                        /* validate value                                */
  1779.     
  1780.     theValueHdr = (TOCValueHdrPtr)value;
  1781.     container = theValueHdr->container->updatingContainer;
  1782.     
  1783.     if ((container->useFlags & kCMWriting) == 0) {
  1784.         Container_Disable(container);
  1785.         ERROR2(CM_err_DeleteIllegal, "value", CONTAINERNAME);
  1786.         return;
  1787.     }
  1788.         
  1789.     if ((theValueHdr->valueFlags & (ValueProtected | ValueUndeletable)) != 0)    { 
  1790.         Container_Disable(container);
  1791.         ERROR1(CM_err_CantDelete3, CONTAINERNAME);
  1792.         return;
  1793.     }
  1794.     
  1795.     if (theValueHdr->useCount > 1) {                                            /* there are other users!                */
  1796.         Container_Disable(container);
  1797.         ERROR1(CM_err_CantDelete4, CONTAINERNAME);                    /* nice try though!                            */
  1798.         return;
  1799.     }
  1800.  
  1801.     /* If the value header is a dynamic value then we must delete both the dynamic value    */
  1802.     /* and its base ("real") value.  These are linked with the base value pointing to the    */
  1803.     /* its dynamic value. For layered dynamic values, each dynamic value is back linked        */
  1804.     /* to its base with the bottom layer having the "real" value as its base.  Only the     */
  1805.     /* bottom layer is actually on the special property chain for dynamic values                     */
  1806.     /* associated with its "real value object.  Only the top layer should ever be passed. */
  1807.     /* We can check for this.  We also remember the "real" value header pointer. You will */
  1808.     /* see how we use this after we get it and do the validation.                                                    */
  1809.  
  1810.     if (IsDynamicValue(theValueHdr)) {                                        /* if we have dynamic value...    */
  1811.         theRealValueHdr = theValueHdr;                                            /* start with current value            */
  1812.         do                                                                                                     /* search for real value...            */
  1813.             theRealValueHdr = DYNEXTENSIONS(theRealValueHdr)->baseValue;
  1814.         while (IsDynamicValue(theRealValueHdr));                        /* loop till we find base value    */
  1815.             
  1816.         if (theRealValueHdr->dynValueData.dynValue == NULL ||    /* do the validation...                */
  1817.                 theRealValueHdr->dynValueData.dynValue != theValueHdr) {
  1818.             Container_Disable(container);
  1819.             ERROR2(CM_err_BadRealValue, "CMDeleteValue", CONTAINERNAME); /* ...oops!                    */
  1820.             return;
  1821.         }
  1822.         
  1823.         /* We now got our hands on the base ("real") value and we're happy with it. By doing*/
  1824.         /* a CMReleaseValue() on the dynamic value we will cause its deletion from there.        */
  1825.         /* It is expected that if the dynamic value is layered, the handlers will                     */
  1826.         /* recursively call CMReleaseValue() on each of their base values down to the bottom */
  1827.         /* layer.  So by the time we return to here all the dynamic values should be gone     */
  1828.         /* for its base value we just found above.  So then all we have to do is delete the    */
  1829.         /* base ("real") value.                                                                                                                            */
  1830.         
  1831.         CMReleaseValue(value);                                                            /* this should kill dynamics        */
  1832.         theValueHdr = theRealValueHdr;                                            /* now set to kill the base            */
  1833.     }    
  1834.     
  1835.     /* If we're recording updates, then define touched list entry for the value delete...    */
  1836.  
  1837.     cmTouchDeletedValue(theValueHdr, theValueHdr->theProperty->theObject);
  1838.     
  1839.     /* Deleted value headers are placed on a chain and marked deleted. The value segments */
  1840.     /* are freed.  Thus any attempted reuse of the refNum will be caught. Although we NULL*/
  1841.     /* out the caller's refNum s/he could have one squirreled away some where.                        */
  1842.     
  1843.     cmMarkValueDeleted(container, theValueHdr, false);
  1844. }
  1845.  
  1846.  
  1847. /*----------------------------------------------------------------------*
  1848.  | CMReleaseValue - destroy association between an value and its refNum |
  1849.  *----------------------------------------------------------------------*
  1850.  
  1851.  The association between the refNum and the value is destroyed. There should be no further
  1852.  operations on the refNum once this routine is called.
  1853.  
  1854.  If the value is a dynamic value, then if there are no other users of the value (i.e., 
  1855.  its use count is 1), the "release" dynamic value value handler is called for the value
  1856.  and ALL of its lower layer dynamic values.  It here are other uses of the dynamic value
  1857.  (i.e., its use count is >1), we do nothing other than decrement the use count by 1.
  1858.  
  1859.  See comments in code below (you can't miss 'em) on how and why we delete all the dynamic
  1860.  value layers.  The release handlers are not expected to release their base value like
  1861.  other value handler operations.
  1862. */
  1863.  
  1864. void CM_FIXEDARGS CMReleaseValue(CMValue value)
  1865. {
  1866.     TOCValueHdrPtr         theBaseValueHdr, dynamicBaseValueHdr, theValueHdr = (TOCValueHdrPtr)value;
  1867.     ContainerPtr              container;
  1868.     DynValueHdrExtPtr extensions;
  1869.     CMHandlerAddr            handler;
  1870.     
  1871.     ExitIfBadValue(value, CM_NOVALUE);                                /* validate value                                        */
  1872.     
  1873.     container = theValueHdr->container;
  1874.  
  1875.     /* As discussed later, dynamic value release handlers must not release their base         */
  1876.     /* value because we do it all here.  To protect agains such "mistakes" we check for        */
  1877.     /* the attempt.  The dynamicBaseValueHdr in the container is normally NULL.  If it         */
  1878.     /* is not NULL it was set when we called a release handler (later) to that handler's    */
  1879.     /* base value.  If it attempts a CMReleaseValue(CMGetBaseValue(value)), we will             */
  1880.     /* intercept it here and yell.  Note, we only test for the base value of the handler.    */
  1881.     /* This lets other releases, which a release handler might want to do, get through.        */
  1882.     /* Since one of those releases could be for yet another dynamic value, we have to            */
  1883.     /* protect the container's version of dynamicBaseValueHdr.  We can do this as a local    */
  1884.     /* variable since a call back to this routine from a release handler is recursive and    */
  1885.     /* won't hurt the up-level copy.                                                                                                            */
  1886.     
  1887.     dynamicBaseValueHdr = container->dynamicBaseValueHdr;/* save current "switch" setting */
  1888.     if (theValueHdr == dynamicBaseValueHdr) {                 /* if attempted release on base...    */
  1889.         Container_Disable(container);
  1890.         ERROR1(CM_err_BaseRelAttempted, CONTAINERNAME);    /* ...yell                                                    */
  1891.         return;
  1892.     }
  1893.  
  1894.     /* Releasing a value that already is released is an error...                                                    */
  1895.     
  1896.     if (theValueHdr->useCount == 0) {
  1897.         Container_Disable(container);
  1898.         ERROR1(CM_err_AlreadyReleased1, CONTAINERNAME);
  1899.         return;
  1900.     }
  1901.     
  1902.     /* Decrement the use count...                                                                                                                    */
  1903.     
  1904.     --theValueHdr->useCount;                                                    /* decrement use count                            */
  1905.     
  1906.     /* If this is not a dynamic value, just exit.  With dynamic values there's just a            */
  1907.     /* "little" bit more to do!                                                                                                                      */
  1908.     
  1909.     if (!IsDynamicValue(value)) return;                                                            
  1910.     
  1911.     /* We have a dynamic value at this point.  If its use count has gone to 0, then it         */
  1912.     /* is time to call the release handler for this value and ALL of its lower layers.      */
  1913.     /* When each handler returns we can delete the value.                                                                    */
  1914.  
  1915.     if (theValueHdr->useCount > 0) return;                        /* dynamic value still in use                */
  1916.     
  1917.     /* The following code calls the release handler for each dynamic value in a layered        */
  1918.     /* set. Each layer is back-linked to its base with the bottom layer having the "real" */
  1919.     /* value as its base. Only the bottom layer is actually on the special property chain */
  1920.     /* for dynamic values associated with its "real" value object.  Only the top layer         */
  1921.     /* should ever be passed.                                                                                                                         */
  1922.     
  1923.     /* The calling of the dynamic value handler is different here from the way all other    */
  1924.     /* dynamic handlers are called for value operations. Here WE CALL EACH DYNAMIC VALUE    */
  1925.     /* HANDLER FOR EACH LAYER EXPLICITLY. A release dynamic handler does NOT do a release */
  1926.     /* on its base value. The reason is that a single "use" (or "new") was done to create */
  1927.     /* a dynamic value.  The CMReleaseValue() to get here is its counterpart.  The user     */
  1928.     /* has no knowledge (sort of) that the type may have had base types that spawned             */
  1929.     /* lower dynamic value layers.                                                                                                                */
  1930.     
  1931.     /* The other reason we do all the releases from here is for safety. We cannot "trust" */
  1932.     /* the handlers to do the release of their base value.  That would leave the data            */
  1933.     /* structures in a weird state.  By processing all the layers ourselves we can be sure*/
  1934.     /* each release handler is called and each dynamic value is properly freed.                     */
  1935.     
  1936.     /* Finally, the following loop does the releases following each dynamic value's back    */
  1937.     /* line.  That means we're releasing in the reverse order of creation.  That seems the*/
  1938.     /* proper order to do it.  If the handlers released their base values, the effect         */
  1939.     /* would be the reverse.                                                                                                                            */
  1940.     
  1941.     do {                                                                                            /* loop up through all the layers...*/
  1942.         extensions             = DYNEXTENSIONS(theValueHdr);        /* save a little code generation        */
  1943.         theBaseValueHdr = extensions->baseValue;                /* we will be deleting theValueHdr    */
  1944.         
  1945.         /* We must now explicitly do the basic guts of a cmGetDynHandlerAddress().  That         */
  1946.         /* routine can't be used because it will go after an inherited release handler if        */
  1947.         /* there is not handler for the value.  We don't want that because we want to call     */
  1948.         /* each release handler explicitly from here -- only if its got one of course.             */
  1949.         
  1950.         /* We know this is the first and only time a full CMReleaseValue() is being done for*/
  1951.         /* this value.  We don't get into here unless the use count goes to 0.  So all we     */
  1952.         /* have to do to see if there is a release handler is to call the value operations    */
  1953.         /* metahandler for this value.    That will tell us if there is a release handler for    */
  1954.         /* this value or it wants to inherit one. If there is a release handler, we call it    */
  1955.         /* in the normal way so that this layer can do any cleanup (e.g., close files, free */
  1956.         /* refCon memory, etc.).  If there is none, everyone is happy (at this layer at         */
  1957.         /* least).  In either case we merge into the delete code that follows it.                        */
  1958.         
  1959.         handler = (CMHandlerAddr)(*extensions->metaHandler)(NULL, (CMGlobalName)CMReleaseValueOpType);
  1960.         
  1961.         /* If we got a non-inherited release handler for this value, call it.  By copying        */
  1962.         /* this value's base value into the container value, dynamicBaseValueHdr, we can        */
  1963.         /* test on entry to this routine for an attempted release on the base value.  We        */
  1964.         /* said above that the rules are that the handler are NOT to do that.                                */
  1965.         
  1966.         if (handler) {                                                                    /* call release handler if we got it*/
  1967.             SignalDynHandlerInUse(theValueHdr, cmReleaseValue);
  1968.             AllowCMGetBaseValue(container);    
  1969.             container->dynamicBaseValueHdr = theBaseValueHdr;
  1970.             extensions->dynValueVector.cmReleaseValue.handler = handler;/* set for the macro    */
  1971.             CMDynReleaseValue(theValueHdr);
  1972.             container->dynamicBaseValueHdr = dynamicBaseValueHdr;
  1973.             DisAllowCMGetBaseValue(container);    
  1974.             SignalDynHandlerAvailable(theValueHdr, cmReleaseValue);
  1975.         }
  1976.         
  1977.         /* Now we must mark the value as deleted. This means moving it to the deletedValues    */
  1978.         /* list.  For the bottom layer we can mark the value in the normal way and remove     */
  1979.         /* the pointer in the "real" value to it.  For the upper layers we must explicitly     */
  1980.         /* move the value to the deletedValues list and mark it. We can tell which is which */
  1981.         /* because cmNewDynamicValue() flagged the upper layers (that was nice of it --         */
  1982.         /* well, this is why it did it, so it wasn't really being nice).                                        */
  1983.         
  1984.         /* Note, when the bottom layer is marked as deleted, the entire "dynamic values"        */
  1985.         /* property will be deleted if this is the last (or only) dynamic value for that        */
  1986.         /* property.  This is a good thing, because we will report an error if we ever see    */
  1987.         /* the "dynamic values" property at continer close time.                                                        */
  1988.         
  1989.         /* Aslo note that freeing the last dynamic value for an object clears the dynamic     */
  1990.         /* value object flag, DynamicValuesObject, which is used to protect against users        */
  1991.         /* deleting the object prematurly.                                                                                                    */
  1992.     
  1993.         if ((theValueHdr->valueFlags & ValueOffPropChain) != 0) {        /* if upper layer...        */
  1994.             if (cmKeepDeletedRefNums(container->toc)) {                             /* ...keep refNums ?        */
  1995.                 theValueHdr->valueFlags |= ValueDeleted;                                /* ...mark value deleted*/
  1996.                 cmInitList(&theValueHdr->valueList);                                        /* ...done for safety        */
  1997.                 cmAppendListCell(&container->deletedValues,theValueHdr);/* ...move to list            */
  1998.             } else                                                                                                        /* if don't keep refNums*/
  1999.                 CMfree(theValueHdr);                                                                        /* ...free it                        */
  2000.         } else {                                                                                                        /* if bottom layer...        */
  2001.             TOCPropertyPtr theProperty = theValueHdr->theProperty;        /* ...point to dyn prop.*/
  2002.             if (cmCountListCells(&theProperty->valueHdrList) == 1)        /* ...if last dyn value    */
  2003.                 theProperty->theObject->objectFlags &= ~DynamicValuesObject;/* ...clear obj flag*/
  2004.             extensions->baseValue->dynValueData.dynValue = NULL;            /* ...fix real value        */
  2005.             cmMarkValueDeleted(container, theValueHdr, false);                /* ...mark value deleted*/
  2006.             if (container->targetContainer)                                                        /* if target not freed    */
  2007.                 if (container->targetContainer->nbrOfDynValues > 0)            /* ...adjust total count*/
  2008.                     --container->targetContainer->nbrOfDynValues;                            
  2009.         }
  2010.         
  2011.         CMfree(extensions);                                                            /* free the extensions                            */
  2012.         
  2013.         theValueHdr = theBaseValueHdr;                                    /* get next layer "down"                        */
  2014.     } while (IsDynamicValue(theValueHdr));                        /* loop down to real value                    */
  2015.     
  2016.     /* We have now deleted all the dynamic value layers for the spawning "real" value.         */
  2017.     /* At this point theValueHdr is pointing at that real value.  When it spawned the            */
  2018.     /* dynamic value(s) we set its use count to 1.  So we "owe" one decrement here.                */
  2019.     
  2020.     if (theValueHdr->useCount > 0)
  2021.         --theValueHdr->useCount;                                                /* decrement real value's use count    */
  2022. }
  2023.                                                           
  2024.                                                             CM_END_CFUNCTIONS
  2025.